Подтвердить что ты не робот

Я могу только передать контравариантного делегата с "как",

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

interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);

class MyClass<T> where T : MyInterface
{
    public void callDelegate(MyFuncType<MyInterface> func)
    {
        MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
        MyFuncType<T> castFunc2 = func as MyFuncType<T>; 
        MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
    }
}

castFunc2 отлично работает, но castFunc1 и castFunc3 вызывают ошибку:

Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'

В статье указано, что castFunc2 и castFunc3 являются "эквивалентными", поэтому я не понимаю, как только один из них мог вызывают ошибку. Еще одна вещь, которая меня смущает, заключается в том, что изменение MyInterface от интерфейса к классу избавляется от ошибки.

Может кто-нибудь помочь мне понять, что здесь происходит? Спасибо!

4b9b3361

Ответ 1

Добавьте ограничение, так что T должен быть классом.

class MyClass<T> where T: class, MyInterface

Это дает компилятору достаточно информации, чтобы знать, что T является конвертируемой. Вам также не требуется явное литье.

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

Причина, по которой работает второй оператор, состоит в том, что as действительно может выполнять нулевое преобразование. Например:

class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
   SomeClass foo = null;
   SomeInterface bar = foo as SomeInterface;
}

Foo, очевидно, не может напрямую конвертироваться в SomeInterface, но он все еще преуспевает, потому что нулевое преобразование все еще может иметь место. Ссылка на MSDN может быть правильной для большинства сценариев, но сгенерированный код IL очень отличается, что означает, что они принципиально отличаются от технической перспективы.

Ответ 2

Эрик Липперт дал большое объяснение этой проблеме в своих недавних сообщениях: "головоломка оператора" , часть первая, "головоломка оператора" , часть вторая.

Основным аргументом в пользу такого поведения является следующее: "is" (или "as" ) операторы - это не то же самое, что и приведение. Оператор "как" может приводить к ненулевому результату, если соответствующий бросок был незаконным, и это особенно верно, когда мы имеем дело с аргументами типа.

В принципе, оператор cast в вашем случае означает (как сказал Эрик), что "Я знаю, что это значение имеет заданный тип, хотя компилятор этого не знает, компилятор должен разрешить его" или "Я знаю, что это значение не относится к данному типу; генерирует специальный, специфичный для типа код для преобразования значения одного типа в значение другого типа."

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

И общие аргументы типа не являются логическими в первом контексте. Если вы имеете дело с аргументом общего типа, то почему вы не четко заявляете этот "контракт", используя аргумент общего типа?

Я не уверен на 100%, чего вы хотите достичь, но вы можете опустить специальный тип из своего метода и вместо этого использовать общий аргумент:

class MyClass<T> where T : MyInterface
{
    public void callDelegate(Action<T> func)
    {
    }
}

class MyClass2
{
    public void callDelegate<T>(Action<T> func)
        where T : MyInterface
    {
    }
}

В противном случае вы должны использовать оператор as с проверкой на null вместо проверки типа.

Ответ 3

В вашем классе указано, что T реализует MyInterface, потому что MyInterface не является типом экземпляра. Поэтому MyFuncType<T> не гарантируется MyFuncType<MyInterface>. Это может быть MyFuncType<SomeType> и SomeType : MyInterface, но это не будет таким же, как SomeOtherType : MyInterface. Есть смысл?