2016-02-06 3 views
-2

Вот классы и интерфейсы у меня есть:C#: Почему мы должны явно применять класс к интерфейсу для доступа к недвусмысленным реализованным методам класса?

public interface Baz { 
    void someMethod(); 
} 
public interface Foo { 
    void someMethod(); 
} 
public class Bar : Foo { 
    void Foo.someMethod(){} 
} 

Вопрос 1) Можно ли удалить интерфейс Foo из объекта Bar, или добавить интерфейс База к объекту Bar во время выполнения возможного?

Если да, могу ли я получить образец короткого кода о том, как это делается?

Если нет, пожалуйста, переходите к вопросу 2 и 3.


Вопрос 2) Почему C# не дает ошибку компиляции при заливке объекта к интерфейсу, он не реализует? Если вы ответите на вопрос 1, нет, mybar никогда не будет Baz, так почему же ошибка компилятора?

void myFunction(){ 
    Bar mybar = new Bar(); 
    (mybar as Baz).someMethod(); 
} 

Вопрос 3) Почему не C# позволяет мне позвонить SomeMethod из объекта Бар без приведения к Foo, даже если он знает, что SomeMethod не является неоднозначным?

void myFunction(){ 
    Bar mybar = new Bar(); 
    mybar.someMethod();//doesn't work 
    mybar.Foo.someMethod();//doesn't work (C# could do this and give a compile error if Bar has a property or public variable with the same name as an interface that it implements to allow for this syntax) 
    (mybar as Foo).someMethod();//works even though someMethod is not ambiguous. 
    //The problem with this is that the keyword "as" has an option to return null, which I want to be very assured will never be the case. 
} 
+0

Технически, компилятор может быть более полезным в обоих случаях, к примеру, так как он знает точный тип mybar, как и в 2, это может привести к ошибке в вызове someMethod, недоступном для кода. Если mybar пришел как параметр, компилятор не знал бы, что mybar является фактическим объектом «Bar», а не «SuperBar», который расширяет как «Bar», так и «Baz».В примере 3, то же самое, он может разрешить вызов someMethod, поскольку он _knows_ точный тип mybar, однако, если он пришел как параметр, вы не знали бы, что объект не также расширяет Baz, делая вызов неоднозначным. –

+0

Сделать более удобным сделать вызов в примере 3 будет особый случай, который очень ограничен, но требует изменения кода для всех случаев, за исключением случаев, когда объект во время компиляции оказывается определенным типом, и, вероятно, сбивает с толку, когда доказательство не может быть выполнено, и компилятор потребует изменения кода для компиляции кода. –

+0

Обратите внимание, что в сценарии, указанном в части 2, это ошибка, если Bar является закрытым классом или структурой. Я подозреваю, что компилятор мог быть написан, чтобы дать ошибку в чрезвычайно простом сценарии, но стоимость считалась запретительной, если сценарий был более сложным (дополнительные переменные, вызовы методов и т. Д.). Поэтому, вместо того, чтобы сделать его сложным или непоследовательным (в зависимости от того, вызываются ли локальные переменные или методы), авторы компилятора выбрали поведение с простым правилом: проверьте преобразование, если тип является типом значения или закрытым классом, и не проверьте в противном случае. –

ответ

0

1) Нет, это не

2) Потому что вы можете сделать что-то вроде этого:

public class Bar2 : Bar, Baz { 
    .... 
} 

void myFunction(){ 
    Bar mybar = someConditionUnknownAtCompileTime? new Bar2(): new Bar(); 
    (mybar as Baz).someMethod(); 
} 

3)

void myFunction(){ 
    Bar mybar = new Bar(); 

    // this method is unavailable without casting to 'Foo' - you can have 
    // many interfaces implemented and everyone can have 'someMethod' defined 
    mybar.someMethod(); 

    // there is no field/property 'Foo' defined in `Bar` 
    mybar.Foo.someMethod(); 

    // everything is OK 
    (mybar as Foo).someMethod(); 
} 
+0

Тогда почему C# не позволяет нам использовать «mybar.Foo.someMethod()» для доступа к методу или свойству, определенному в Foo, и дать ошибку компиляции, если какой-либо метод или свойство в Bar имеет имя «Foo», поэтому что нам никогда не понадобится делать избыточный «if (myvar is Foo)» проверить или оставить и подождать, пока он не выкинет исключение NullReferenceException в отгруженном программном обеспечении? –

+0

@RyanAWE Это вопрос синтаксиса. 'Class.ImplementedInterface.Member' не допускается. У вас может быть поле с именем 'Foo', определенное в' Bar', и компилятор не мог знать, чего вы хотите достичь. – romanoza

1

Невозможно добавить или удалить реализации интерфейса для любых объектов во время выполнения.

Оператор as проверяет тип объекта на времени выполнения и возвращает ссылку на него как на этот тип. В этом случае возвращается null и выдается NullReferenceException.

mybar.Foo.someMethod() обращается к члену по телефону Foo по телефону mybar, но нет такого члена. Объявление, void Foo.someMethod(){}, просто означает, что метод определенно определен для интерфейса Foo.

+0

Если во время компиляции C# уже знает, что mybar никогда не может быть Baz, почему C# даже потрудится построить этот код без ошибок? –

+0

Оператор 'as' предназначен для проверки только времени выполнения. Обычный оператор литья (например, '(Baz) myBar') даст вам сообщение об ошибке или предупреждении. –

+1

@RyanAWE Компилятор не знает, что во время компиляции. См. Пример в моем ответе. – romanoza

0

объявлений. 1) Нет, это невозможно. Я думаю, что для этого требуется динамическое программирование.

Объявление. 2) Оператор as создан для литья ссылочных типов во время выполнения, но он не даст вам исключения, но вместо него. В случае, если вы написали, это вызовет исключение, но потому, что вы хотите вызвать метод на null. Это может быть опущено: (mybar as Foo)?.someMethod()

Ad 3) Это всего лишь вопрос синтаксиса, не двусмысленность :)