убедить компилятор C #, что выполнение прекратится после возвращения члена - PullRequest
4 голосов
/ 24 марта 2010

Я не думаю, что это в настоящее время возможно, или если это даже хорошая идея, но я думал об этом только сейчас. Я использую MSTest для модульного тестирования моего C # проекта. В одном из моих тестов я делаю следующее:

MyClass instance;

try
{
    instance = getValue();
}
catch (MyException ex)
{
    Assert.Fail("Caught MyException");
}

instance.doStuff(); // Use of unassigned local variable 'instance'

Чтобы этот код компилировался, мне нужно присвоить значение instance либо в его объявлении, либо в блоке catch. В качестве альтернативы я мог бы return после Assert.Fail, но это все еще обходной путь вместо компилятора, просто зная , что выполнение не может продолжаться после этой точки. Насколько мне известно, Assert.Fail никогда не позволит выполнить выполнение после него, поэтому instance никогда не будет использоваться без значения. Почему тогда я должен присвоить ему значение? Если я изменю Assert.Fail на что-то вроде throw ex, код скомпилируется нормально, я предполагаю, потому что он знает, что исключение не допустит выполнения до точки, где instance будет использоваться неинициализированным.

Наоборот, что если я не хочу, чтобы тест провалился, а был помечен как неокончательный? Я мог бы сделать Assert.Inconclusive вместо Fail, и было бы неплохо, если бы компилятор знал, что выполнение не будет продолжаться после этого.

Так это случай, когда знания времени выполнения или времени компиляции о том, где будет разрешено выполнение выполнения? Будет ли когда-либо разумным для C # говорить, что член, в данном случае Assert.Fail, никогда не разрешит выполнение после его возврата? Может быть, это может быть в форме атрибута метода. Будет ли это полезным или ненужной сложностью для компилятора?

Внешние юнит-тесты

Поскольку люди [справедливо] указывают, что это глупый способ написания модульного теста, рассмотрим мой вопрос за пределами области модульного тестирования:

MyClass instance;

if (badThings)
{
    someMethodThatWillNeverReturn();
}
else
{
    instance = new MyClass();
}

instance.doStuff();

Здесь потенциально я мог бы заменить вызов someMethodThatWillNeverReturn на выдачу исключения, и, возможно, если бы у меня было что сделать, я мог бы сделать это в конструкторе для исключения.

Решарпер знает

Если я добавлю return после Assert.Fail или Assert.Inconclusive, Resharper окрашивает return в серый цвет и имеет всплывающую подсказку «Код эвристически недоступен».

Ответы [ 5 ]

3 голосов
/ 24 марта 2010

Да, было бы разумно иметь что-то, что указывало бы на то, что участник никогда не закончил бы нормально - т.е. утверждал, что точка после члена была недоступна. (Это может быть либо из-за исключения, либо из-за зацикливания навсегда.)

Вы бы хотели, чтобы было что-то (будь то в CLR или в компиляторе), чтобы составить план резервного копирования, если вы ошибаетесь: что произойдет, если кто-то изменит Assert.Fail на вернуть нормально? Возможно, вы захотите, чтобы часть проверки кода была проверена, и она никогда не вернется нормально.

Я полагаю, что есть сообщение в блоге об этой идее от кого-то в Microsoft ... Я посмотрю, смогу ли я найти его.

С точки зрения синтаксиса для его представления, хотя атрибут является очевидной идеей, мне очень нравится идея возвращаемого типа «никогда». Очевидно, что это может привести к конфликту с существующими типами «никогда», но эй ...

С точки зрения его полезности: очевидный обходной путь - бросить исключение сразу после утверждения, но это, безусловно, раздражает. (Обычно это лучше, чем return, поскольку это означает, что если метод, в котором вы пишете это, имеет тип возвращаемого значения, вам не нужно указывать бессмысленное возвращаемое значение - и вам также не нужно проверять, что все out параметрам присвоены значения.) Так что не требуется - но я думаю, что это было бы неплохо. Является ли это самой важной вещью, которую команда C # может сделать со своим ограниченным бюджетом, - это другой вопрос - просто превзойти Эрика;)

1 голос
/ 24 марта 2010

Одна вещь, которая меня интересует: как бы среда обработала этот код, если бы вы вручную скомпилировали и выполнили его? Я считаю, что он проверяет ошибки в коде IL, может быть, он также ловит эту "ошибку" ... может и нет :)

1 голос
/ 24 марта 2010

throw ex после Assert.Fail? Или, что еще лучше, просто полностью удалите try/catch и Assert.Fail в этом случае, и пусть неперехваченное исключение не пройдет тестовый модуль для вас.

0 голосов
/ 24 марта 2010

Видя, что вы утверждаете Fail для сгенерированного исключения, почему бы вам не переместить весь тест в попытку или не избавиться от попытки и не дать автоматически сгенерированному исключению заявить об ошибке.

try
{
    MyClass instance;
    instance = MyClass.getValue();
    instance.doStuff();
}
catch (Exception ex)
{
    Assert.Fail("Caught MyException");
}

... или ...

MyClass instance;
instance = MyClass.getValue();
instance.doStuff();
0 голосов
/ 24 марта 2010

return после Assert.Fail()

...