15

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?

2
  • I do not understand what you are really trying to achieve with this. MyClass does always use an IFoo object (interfaces and implementations) or can it use anything else ? Anyway, if you are trying to implicitly convert an interface, it's impossible in C# (as @EricLippert has already said) Commented Sep 21, 2016 at 14:30
  • There will be multiple IFoo implementations (Foo1, Foo2,.. FooN). There might also be other interfaces Bar(OtherInterface other) { Bar2<OtherInterface>(other); } The question of course is a stripped down version of the actual problem. Commented Sep 21, 2016 at 14:36

2 Answers 2

28

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.

Sign up to request clarification or add additional context in comments.

3 Comments

There is Foo1, Foo2, ..., FooN. The point would be that MyClass shouldn 't care because Bar() would not know. (There could be FooX in the future). So casting to the actual run-time type isn't an option.
Well indeed. If I write ´void Bar(MyClass<IFoo> foo)´ I could inject any IFoo implementation (Foo1, Foo2, .., FooN, FooX) since at that point the implicit conversion will be valid...
what about this situation (specifically for unity) Map<T> (which just encloses a T[,]) and I want to be able to write public static implicit operator Texture2D(Map<Color> map), public static implicit operator Texture2D(Map<float> map) and public static implicit operator Texture2D(Map<byte> map) methods but I'm getting CS0556 errors, is there a way to fix this issue?
2

Assign using the 'dynamic' keyword. You can differentiate it later.

  var hook = Environment.Version < new Version(4, 0) ? (dynamic)
    // .NET 2.0->3.5        
    new JITHook<MscorjitAddrProvider>() :
    // .NET 4.0+
    new JITHook<ClrjitAddrProvider>();

1 Comment

Confirmed. For me, doing (MyClass)(otherClassObject as dynamic), where otherClassObject was an interface type triggered the implicit cast defined on MyClass. WIthout the as dynamic the cast was not triggered.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.