Статические классы С# и оператор-оператор
После рефакторинга некоторого кода в последнее время, в котором участвовали некоторые переименования классов, некоторые из моих кодов неожиданно разразились. Причиной был неудачный оператор "есть", что я был очень удивлен, не был ошибкой или предупреждением компилятора.
Эта полная программа показывает ситуацию:
static class ExtensionMethods {}
class Program {
static void Main() {
Test("Test");
}
public static bool Test(object obj)
{
return obj is ExtensionMethods;
}
}
Я бы предположил, что "obj is ExtensionMethods" вызывает какое-то предупреждение, учитывая, что ExtensionMethods является статическим классом.
Компилятор выдаст предупреждение для оператора "is", если объект, находящийся в процессе тестирования, никогда не может быть предоставленного типа, например ((string)obj) is System.Uri
.
Я забыл сценарий, в котором это будет действительно значимым тестом?
Ответы
Ответ 1
Я был очень удивлен, что это не ошибка компилятора или предупреждение.
Это должно было быть. Это был недосмотр.
Было множество ошибок, подобных статическим классам. Если я правильно помню, был даже какой-то причудливый сценарий, который Владимир Решетников нашел там, где можно было сделать вывод о типе статического типа как привязку к параметру типа.
По-видимому, этот, который я видел раньше, никогда не фиксировался. Извинения за надзор.
Я забыл сценарий, в котором это будет действительно значимым тестом?
Нет.
Ответ 2
Из спецификации С# 3.0, раздел 10.1.1.3:
Статический класс не может включать спецификацию класса (§10.1.4) и не может явно указывать базовый класс или список реализованных интерфейсов. Статический класс неявно наследуется от объекта типа.
Поэтому компилятор, по-видимому, не вызывает предупреждения, потому что он не знает, что is
всегда будет возвращать false. (Статический класс "есть" object
, и поэтому компилятор не знает, что object
"is" или "is" не является статическим классом во время компиляции.) Реально он, вероятно, знает или, по крайней мере, может узнайте, но, по-видимому, он не специализируется на этом случае и проверке.
Ответ 3
Я сделал трещину в этом, и хотя я не могу найти это в ссылке MSDN, похоже, что это оператрон зависит от того, как создать экземпляр типа, который можно проверить. Поскольку статические классы не могут быть установлены (поскольку статические классы являются объектами, созданными в стеке программ во время компиляции)...
Например, если вы сделаете следующее, вы получите следующую ошибку: "Невозможно объявить переменную статического типа"
ExtensionMethods ex;
Если вы выполните ниже, вы получите следующую ошибку: "Невозможно создать экземпляр статического класса"
ExtensionMethods ex2 = new ExtensionMethods();
Чтобы продемонстрировать эту проблему, вот полная программа, показывающая, что оператор is.
static class ExtensionMethods { }
// notice non-static
class AnotherNonStaticExtensionMethod { }
class Program
{
static void Main(string[] args)
{
Debug.WriteLine(Test(new AnotherNonStaticExtensionMethod()).ToString());
Debug.WriteLine(Test("Test").ToString());
Debug.WriteLine(Test(4).ToString());
}
public static bool Test(object obj)
{
if (obj is ExtensionMethods)
{
return true;
}
else if (obj is AnotherNonStaticExtensionMethod)
{
return true;
}
else
{
return false;
}
}
}
Вот следующий вывод:
True
False
False
Объект способен проверять экземпляр класса с первым утверждением, что заставляет меня полагать, что оператор зависит от него. Надеюсь, кто-нибудь сможет это подтвердить?
Предоставлено NominSim::
Из спецификации С# 3.0, раздел 10.1.1.3:
Статический класс не может включать спецификацию класса (§10.1.4) и не может явно указывать базовый класс или список реализованных интерфейсы. Статический класс неявно наследует от объекта типа.
Ответ 4
Ответ Эрика Липперта с 2013 года объясняет, что это было ошибкой в компиляторе Visual С# 5.0 (и некоторых более ранних версиях). Проблема была и с оператором as
, например object bad = obj as ExtensionMethods;
.
В С# 6.0 (с 2015 года) и позже вы получаете ошибку времени компиляции (а не только предупреждение):
ошибка CS7023: второй операнд оператора 'is' или 'as' может не быть статическим типом "Xxxx"
Однако это верно только в том случае, если вы указали особенность Strict, см. другой поток, чтобы узнать, как это сделать.
Ответ 5
В соответствии с Спецификацией языка С#:
Оператор is используется для динамической проверки, совместим ли тип времени выполнения с данным типом. Результатом операции E является T, где E является выражением, а T является типом, является логическим значением, указывающим, может ли E быть успешно преобразован в тип T посредством эталонного преобразования, преобразования бокса или преобразования для распаковки.
и
Статический класс не может включать спецификацию класса (§10.1.4) и не может явно указывать базовый класс или список реализованных интерфейсов. Статический класс неявно наследует от объекта типа.
Поскольку он наследует от System.Object
неявно, имеет смысл, что компилятор не выдает предупреждение.
Проверка:
var staticBaseType = typeof(B).BaseType;
В качестве базового типа вы получите System.Object
.