Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implicit operator with generic not working for interface

I basically have the following class (example found on C# creating an implicit conversion for generic class?).

class MyClass<T>
{
  public MyClass(T val)
  {
     Value = val;
  }

  public T Value { get; set; }

  public static implicit operator MyClass<T>(T someValue)
  {
     return new MyClass<T>(someValue);
  }

  public static implicit operator T(MyClass<T> myClassInstance)
  {
     return myClassInstance.Value;
  }
}

One could do

MyClass<IFoo> foo1 = new Foo();
MyClass<Foo>  foo2 = new Foo();

//But not
MyClass<IFoo> foo3 = (IFoo)new Foo();

The real issue occurs when trying to do something like

void Bar(IFoo foo)
{
    Bar2(foo);
    //What should be the same as
    Bar2<IFoo>(new MyClass<IFoo>(foo));
}

void Bar2<T>(MyClass<T> myClass)
{
     //Do stuff
}

How could I refactor MyClass so it would be possible to work with objects when only the interface is known?

like image 801
Stijn Van Antwerpen Avatar asked Sep 21 '16 14:09

Stijn Van Antwerpen


1 Answers

Short answer:

User-defined implicit conversions do not work on interfaces. Don't try to make it work. Find another solution to your type system problem.

Long answer:

This is a deliberate decision of the C# design team. The principle is that when you are making a conversion involving an interface you want to preserve referential identity; you are asking about the identity of the object that implements the interface, not trying to create a similar object that has similar properties.

The larger principle here is that a user-defined conversion should not replace a built-in conversion. But since almost any class can be subclassed, and that subclass can implement just about any interface, it is very hard to know statically whether a given user-defined conversion involving an interface might be replacing a built-in conversion.

FYI this is a particularly tricky bit of the specification, and the C# compiler has some bugs here. I suspect that one of your cases above takes advantage of these bugs; the fact that there are real-world programs which do so is what prevented me from fixing the bugs.

The bugs are mostly a consequence of this feature being designed before generics, and then not redesigned sufficiently after generics introduced many unforeseen complications.

For details, see my extensive comments here, particularly the bits marked DELIBERATE SPEC VIOLATION that describe problems with interface conversions.

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs

As you can see, this file is less than a thousand lines long, and probably more than half comments. It took weeks of careful research and many discussions with the language team to get these semantics sorted out. Once you make a mistake in a compiler, you often have to understand it thoroughly a decade later, and then enshrine it forever so as to not break a customer on upgrade. There are many object lessons to language designers in how C# messed up this obscure part of the specification.

How could I refactor MyClass so it would be possible to work with objects when only the interface is known?

Don't try. Cast the interface reference to the actual runtime type and then work with it from there. Or create an instance of the desired type explicitly, rather than by implicit conversion. Don't try to play games with implicit conversions and interfaces; it will not work out well.

like image 188
Eric Lippert Avatar answered Oct 03 '22 06:10

Eric Lippert