Еще один вопрос «Использование неназначенной локальной переменной« что угодно »» - PullRequest
4 голосов
/ 30 августа 2011

Это то, о чем я уже давно задумываюсь.Я просмотрел кучу вопросов, в которых содержится ошибка, приведенная ниже, в названии, но не смог найти вопрос, объясняющий этот случай.

Сначала посмотрите на этот пример:

private void test() {
    string errorMessage;
    bool isOK = SomeClassWithBusinessRules.VerifySomeStuff(idOfStuffToVerify, ref errorMessage);
    if (!isOK)
        throw new BusinessException(errorMessage ?? "Some error occured.");
}

Если вы скомпилируете этокомпилятор будет жаловаться на это сообщение:

Ошибка 2 Использование неназначенной локальной переменной 'errorMessage'

Изменение инициализатора переменной на null заставит его исчезнуть.

Это скомпилирует:

private void test() {
    string errorMessage = null;
    bool isOK = SomeClassWithBusinessRules.VerifySomeStuff(idOfStuffToVerify, ref errorMessage);
    if (!isOK)
        throw new BusinessException(errorMessage ?? "Some error occured.");
}

Так почему мы получаем ошибку компиляции?

Ответы [ 6 ]

14 голосов
/ 30 августа 2011

Когда вы передаете его в VerifySomeStuff, вы указываете ref, но у него еще нет значения. Это недопустимо, поскольку VerifySomeStuff может выбрать чтение значения, которое не имеет определенного значения на данный момент. Назначение null удовлетворяет требованию определенного присвоения. Альтернативой будет out:

string errorMessage;
bool isOK = SomeClassWithBusinessRules.VerifySomeStuff(id, out errorMessage);
if (!isOK)
    throw new BusinessException(errorMessage ?? "Some error occured.");

, что было бы допустимо (но требует изменений на VerifySomeStuff, поскольку подпись должна быть изменена, и теперь требуется для присвоения значения параметру перед выходом (если не происходит исключение)). Из показанного кода вы можете выбрать для VerifySomeStuff присвоение null параметру, если ошибки нет.

Конечно, если bool и string тогда дублировать цель "была ли проблема", вы также можете использовать:

string errorMessage = SomeClassWithBusinessRules.VerifySomeStuff(id);
bool isOK = errorMessage == null;
13 голосов
/ 30 августа 2011

Вы получаете ошибку компиляции, потому что переменная, используемая в качестве аргумента ref, должна быть обязательно назначена заранее. Если вы измените метод на использование параметра out, все будет хорошо:

bool isOK = SomeClass.VerifySomeStuff(id, out errorMessage);

Обратите внимание, что для этого также необходимо изменить значение VerifySomeStuff, чтобы сделать его параметром out. В этом случае метод должен был бы определенно назначить значение в любом «нормальном» пути возврата - так что к тому времени, когда метод возвращается нормально, errorMessage определенно назначается.

См. Разделы 10.6.1.2 и 10.6.1.3 подробностей параметров ref и out соответственно.

6 голосов
/ 30 августа 2011

Вы передаете errorMessage по ref. Это имеет семантику входа / выхода. Другими словами, протокол заключается в том, что получатель может ожидать инициализации объекта, чего вы не сделали.

Кажется, вы просто хотите out семантику. Измените VerifySomeStuff на out вместо ref для errorMessage, а также измените код вызова на out.

Обратите внимание, что при передаче с использованием out вызываемому пользователю не разрешается читать, пока объект не будет инициализирован. Вызываемый также обязан выполнить эту инициализацию перед возвратом.

5 голосов
/ 30 августа 2011

Вы также можете исправить «ошибку», изменив метод на

 SomeClassWithBusinessRules.VerifySomeStuff(int idOfStuffToVerify, 
            out string  errorMessage);

Когда вы используете out, нагрузка переходит на метод, он не будет компилироваться, если вы не сделаете присвоение параметру errorMessage.

2 голосов
/ 30 августа 2011

На ваш вопрос (почему компилятор жалуется) уже отвечали другие.Однако я хотел бы предложить вам пересмотреть свой дизайн:

bool isOK = SomeClassWithBusinessRules.VerifySomeStuff(idOfStuffToVerify, ref errorMessage);
if (!isOK)
    throw new BusinessException(errorMessage ?? "Some error occured.");
}

Поскольку errorMessage требуется только при возникновении ошибки, зачем вам дополнительное возвращаемое значение?Вы могли бы сократить это до:

string error = SomeClassWithBusinessRules.VerifySomeStuff(idOfStuffToVerify);
if (error != null)
    throw new BusinessException(error);
}

(Конечно, тогда у вас больше не возникает случай "Произошла какая-то ошибка". Но показ "бесполезных" сообщений об ошибках в любом случае является плохой практикой.)

Фактически, если ошибка является исключительным обстоятельством (т. Е. Чем-то, что не является частью обычного потока управления, а скорее чем-то, указывающим на ошибку данных или логическую ошибку), может иметь смысл переместить исключение внутри VerifySomeStuff:

// no return value
SomeClassWithBusinessRules.VerifySomeStuff(idOfStuffToVerify);

class SomeClassWithBusinessRules {
    void VerifySomeStuff(int id) {
        ...
        if (someCondition)
            throw new BusinessException(error);
        ...
    }
}
2 голосов
/ 30 августа 2011

При передаче аргумента ref одна из вещей, которые может сделать вызываемый код, - перенаправить переменную аргумента ref на новое место, то есть обновить ее ссылку.Чтобы это произошло, переменная должна указывать на что-то в первую очередь, поэтому она должна быть назначена, даже если это просто null.

...