0

Here are the classes and interfaces I have:

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

Question 1) Is it possible to remove Foo interface from a Bar object, or add a Baz interface to a Bar object during runtime possible?

If yes, can I have a short code sample on how it is done?

If no, kindly proceed to question 2 and 3.


Question 2) Why does C# not give a compile error when casting an object to an interface that it does not implement? If you answer to question 1 is no, mybar would never be a Baz, so why is there no compiler error?

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

Question 3) Why doesn't C# allow me to call someMethod from a Bar object without casting to Foo, even if it knows that someMethod is not ambiguous?

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.
}
Ryan A WE
  • 59
  • 1
  • 10
  • Technically, the compiler could be more helpful in both cases, for example since it knows the exact type of mybar as in 2 it could give an error about the call to someMethod being unreachable code. If mybar came in as a parameter though, the compiler would not know that mybar was an actual `Bar` object and not a `SuperBar` that extends both `Bar` and `Baz`. In example 3, same thing, it could allow the call to someMethod since it _knows_ the exact type of mybar, however if it came in as a parameter you'd not know that the object didn't also extend Baz making the call ambiguous. – Joachim Isaksson Feb 06 '16 at 08:27
  • Making it more convenient to make the call in example 3 would be a special case that is very limited though, requiring code changes for all cases except when the object is proven to be a specific type at compile time, and probably confusing when the proof cannot be done and the compiler would require a code change to compile the code. – Joachim Isaksson Feb 06 '16 at 08:35
  • Note that in the scenario given in part 2, it is an error if Bar is a sealed class or struct. I suspect that the compiler could have been written to give an error in the extremely simple scenario, but the cost was considered prohibitive if the scenario were any more complicated (additional variables, method calls, etc.). So rather than make it complicated or inconsistent (depending on whether local variables or methods are called), the compiler authors opted for the behavior with a simple rulen: check the conversion if the type is a value type or sealed class, and don't check otherwise. – Mike Zboray Feb 06 '16 at 09:02

3 Answers3

1

It is not possible to add or remove interface implementations to any objects at runtime.

The as operator checks for the object's type at runtime and returns a reference to it as that type. In this case, null will be returned and a NullReferenceException is thrown.

mybar.Foo.someMethod() accesses a member called Foo on mybar but there is no such member. The declaration, void Foo.someMethod(){}, just means that the method is explictly defined for the Foo interface.

Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
  • If during compile time C# already knows that mybar can never be a Baz, why does C# even bother to build this code without errors? – Ryan A WE Feb 06 '16 at 07:55
  • The `as` operator is meant to be a runtime-only check. The normal casting operator (e.g., `(Baz) myBar`) will give you an error or warning. – Mark Cidade Feb 06 '16 at 08:08
  • 1
    @RyanAWE There is no way the compiler could know that at compile time. See the example in my answer. – romanoza Feb 06 '16 at 08:11
  • 1
    @RyanAWE: Just because an object is a `Bar` doesn't mean it's not a `Baz`. – recursive Feb 06 '16 at 08:21
0

1) No, it isn't

2) Because you can do something like this:

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(); 
}
romanoza
  • 4,775
  • 3
  • 27
  • 44
  • Then why doesn't C# allow us to use "mybar.Foo.someMethod()" to access the method or property defined in Foo, and give a compile error if any method or property in Bar has the name "Foo", so that we will never be required to do a redundant "if(myvar is Foo)" check, or leave and wait until it throws a NullReferenceException in a shipped software? – Ryan A WE Feb 06 '16 at 08:02
  • @RyanAWE It's a matter of syntax. `Class.ImplementedInterface.Member` is not allowed. You can have a field named `Foo` defined in `Bar` and the compiler couldn't know what you want to achieve. – romanoza Feb 06 '16 at 08:08
0

Ad. 1) No, it is impossible. I think this requires dynamic programming.

Ad. 2) as operator is made for casting a reference types in runtime but it won't give you a Exception, but a null instead. In case you wrote, it will raise an exception but because you want to invoke a method on null. This can be omitted: (mybar as Foo)?.someMethod()

Ad 3) It is just a matter of syntax, not ambiguity :)

badsamaritan
  • 407
  • 3
  • 9