2011-01-31 2 views
0

Недавно я столкнулся с странной проблемой, связанной с кастингом. Каждое обсуждение/сообщение, которое я видел, имеет тенденцию вращаться вокруг, используя литье, когда вы уверены в том, что объект будет выпущен, а также несколько деталей. Однако я не нашел, что аргументы за кодом ниже:Разница между оператором и литьем (дженерики)

class Program 
{ 
    static void Main(string[] args) 
    { 
     var h = new SomeCommandHandler(); 
     var c = h as ICommandHandler<ICommand>; //this works as expected 
     //var c = (ICommandHandler<ICommand>)h; //this throws - why? 
    } 

    interface ICommand { } 
    class SomeCommand : ICommand { } 

    interface ICommandHandler<I> where I : ICommand { } 
    class SomeCommandHandler : ICommandHandler<SomeCommand> { } 
} 

Так почему же второй вызов выдает исключение? В чем разница между кастингом и оператором, о котором я не знаю?

EDIT: Он wpuld бросить в комментировал линии выше "Необработанное исключение: System.InvalidCastException: Не удается привести объект типа 'SomeCommandHandler' к типу 'ICommandHandler`1 [ConsoleApplication1.Program + ICommand]'"

+0

@thecoop - рискуя догадку Это InvalidCastException ... –

ответ

9

Ну, вот и вся разница. Оператор as возвращает null, если объект не может быть отнесен к этому типу, а просто литье создает исключение.

+0

Errr не знаю, как я проглядел это. Я был уверен, что «как» из примера возвратил ненулевое значение ... –

+0

Идеальный ответ +1 –

0

Листинг не выполняется, потому что ICommandHandler<SomeCommand> не является ICommandHandler<ICommand>. См. here для получения более подробной информации. & примеры.

as просто возвращает нуль, когда экземпляр не указанного типа, в то время как литая бросает InvalidCastexception

1

Это вызывает исключение, поскольку ч имеет тип SomeCommandHandler который ICommandHandler<SomeCommand> и вы пытаетесь бросить его ICommandHandler<ICommand> это другой тип.

0

переменная c является нулевой. Он не бросает, потому что это то, что использует «как». Другими словами, экземпляр h не является экземпляром ICommandHandler.

Следующая строка бросает, потому что вы пытаетесь заставить бросок экземпляра SomCommandHandler к экземпляру ICommandHandler

имеет смысл?

2

Другие уже объяснили разницу между непосредственным бросанием исключения и as, возвращающим null при сбое при отказе. Для того, чтобы иметь возможность сделать такой бросок успехом вам нужно будет сделать общий интерфейс контравариантным:

interface ICommandHandler<out I> where I : ICommand { } 

Однако, это не может быть возможно, в зависимости от того, как интерфейс действительно выглядит (я предполагаю, что вы показываете сокращенную версию для краткости). Если ваш интерфейс содержит метод, который принимает аргумент типа I, это не сработает; тип I должен появиться только в GET-операции:

interface ICommandHandler<out I> where I : ICommand 
{ 
    void SetCommand(I n); // this would not be allowed... 
    I GetCommand();  // ...but this would. 
} 
+0

Угадай. Мне нужно это как параметр (void Execute (SomeCommand cmd)), поэтому, как вы сказали, ковариация мне не поможет. Спасибо за помощь в любом случае! –

Смежные вопросы