Implicit Operator Using Interfaces

implicit operator using interfaces

The reason you can't do this is because it is specifically forbidden in the C# language specification:

Source: ECMA-334 Section 15.10.4

A class or struct is permitted to
declare a conversion from a source
type S to a target type T provided all
of the following are true:

  • ...
  • Neither S nor T is object or an interface-type.

and

User-defined conversions are not
allowed to convert from or to
interface-types. In particular, this
restriction ensures that no
user-defined transformations occur
when converting to an interface-type,
and that a conversion to an
interface-type succeeds only if the
object being converted actually
implements the specified
interface-type.

Defining implicit and explicit casts for C# interfaces

You cannot define casts or operator overloading on interfaces. Since an interface is a contract that describes the members which will always be available (either as an explicit cast to that interface or as public members) and nothing more you cannot rely on interfaces to contain any sort of built in logic such as how to cast or how operators will perform with that interface.

You can still inherit from an abstract base class which implements the interface and provides the logic you need for casts or operator overloading. This doesn't violate interface oriented design. Classes which do not inherit from the common base class but implement the interface will still need to independently implement their own implicit casts and operator overloads. If you wish to centralize the logic for working with classes that commonly implement an interface you can do so in C# 3.0+/.NET Fx 3.5 with extension methods (or in previous versions with static methods). Below I demonstrate this with a utility class and two classes, Foo and Bar, which don't have a common ancestor. They share the code which comprises the utility function Add so you don't have to repeat this implementation in both classes.

public interface IInterface
{
int X { get; set; }
int Y { get; set; }
}

public static class IInterfaceTHelper
{
public static IInterface Add<T>(this IInterface a, IInterface b)
where T : new()
{
var ret = (IInterface)new T();
ret.X = a.X + b.X;
ret.Y = a.Y + b.Y;
return ret;
}
}

class Foo : IInterface
{
public int X { get; set; }
public int Y { get; set; }

public static IInterface operator +(Foo a, IInterface b)
{
return a.Add<Foo>(b);
}
}

class Bar : IInterface
{
public int X { get; set; }
public int Y { get; set; }

public static IInterface operator +(Bar a, IInterface b)
{
return a.Add<Bar>(b);
}
}

class Program
{
static void Main(string[] args)
{
var foo = new Foo { X = 5, Y = 3 };
var bar = new Bar { X = 3, Y = 5 };

var result = foo + bar;
Console.WriteLine(result.GetType().Name + " " + result.X + " " + result.Y);
result = bar + foo;
Console.WriteLine(result.GetType().Name + " " + result.X + " " + result.Y);

Console.ReadLine();
}
}

If your interfaces contained more than just contracts that would be violating design by contract.

Implicit operator to an interface through an adapter in C#

The reason the error occurs is that, yes there is a cast possible, but you are not indicating anywhere (in your method Test2) that the conversion should be done using the ClassAToInterfaceAdapter class. You could have a dozen implicit operators defined in as many classes, but that is no reason to use them.

You have to indicate the type, and if the type has an implicit operator, than it will be used. This works because the type ClassAToInterfaceAdapter contains the implicit operator:

public void Test2() {
var classA = new ClassA();
ClassAToInterfaceAdapter i = classA;
}

Edit:

From another SO answer

The reason you can't do this is because it is specifically forbidden in the C# language specification:

A class or struct is permitted to
declare a conversion from a source
type S to a target type T provided all
of the following are true:

  • ...
  • Neither S nor T is object or an interface-type.

and

User-defined conversions are not
allowed to convert from or to
interface-types. In particular, this
restriction ensures that no
user-defined transformations occur
when converting to an interface-type,
and that a conversion to an
interface-type succeeds only if the
object being converted actually
implements the specified
interface-type.

Source

implicit operator with generic not working for interface

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.

Interfaces, Inheritance, Implicit operators and type conversions, why is it this way?

You have basically asked the compiler to do this:

  1. I have this: DbRecurPatt
  2. I want this: IRecurrencePattern
  3. Please figure out a way to get from point 1. to point 2.

The compiler, even though it may only have one choice, does not allow you to do this. The cast operator specifically says that DbRecurPatt can be converted to a RecurrencePattern, not to a IRecurrencePattern.

The compiler only checks if one of the two types involved specifies a rule on how to convert from one to the other, it does not allow intermediary steps.

Since no operator has been defined that allows DbRecurPatt to be converted directly to IRecurrencePattern, the compiler will compile this as a hard cast, reinterpreting the reference as a reference through an interface, which will fail at runtime.

So, the next question would be this: How can I then do this? And the answer is you can't.

The compiler does not allow you to define a user-defined conversion operator to or from an interface. A different question here on Stack Overflow has more information.

If you try to define such an operator:

public static implicit operator IRecurrencePattern(DbRecurPatt obj)
{
return new RecurrencePattern() { Data = $"{obj.Name} - {obj.Description}" };
}

The compiler will say this:

CS0552

'DbRecurPatt.implicit operator IRecurrencePattern(DbRecurPatt)': user-defined conversions to or from an interface are not allowed

Why isn't it possible to define implicit cast operator from interface to class?

What if you had a subclass of Control, and that subclass implemented the ISomeControl interface.

class SomeControl : Control, ISomeControl {}

Now a cast would be ambiguous -- the built-in upcast, and your user-defined conversion. So you can't provide user-defined conversions for interfaces.

More on implicit conversion operators and interfaces in C# (again)

The context of your example, it won't work again because the implicit operator has been placed against an interface... I'm not sure how you think your sample is different to the one you linked other than you try to get one concrete type across to another via an interface.

There is a discussion on the topic here on connect:

http://connect.microsoft.com/VisualStudio/feedback/details/318122/allow-user-defined-implicit-type-conversion-to-interface-in-c

And Eric Lippert might have explained the reason when he said in your linked question:

A cast on an interface value is always treated as a type test because
it is almost always possible that the object really is of that type
and really does implement that interface. We don't want to deny you
the possibility of doing a cheap representation-preserving conversion.

It seems to be to do with type identity. Concrete types relate to each other via their hierarchy so type identity can be enforced across it. With interfaces (and other blocked things such as dynamic and object) type identity becomes moot because anyone/everyone can be housed under such types.

Why this is important, I have no idea.

I prefer explicit code that shows me I am trying to get a Foo from another that is IFooCompatible, so a conversion routine that takes a T where T : IFooCompatible returning Foo.

For your question I understand the point of discussion, however my facetious response is if I see code like Foo f = new Bar() in the wild I would very likely refactor it.


An alternative solution:

Don't over egg the pudding here:

Foo f = new Bar().ToFoo();

You have already exposed the idea that Foo compatible types implement an interface to achieve compatibility, use this in your code.


Casting versus converting:

It is also easy to get wires crossed about casting versus converting. Casting implies that type information is integral between the types you are casting around, hence casting doesn't work in this situation:

interface IFoo {}
class Foo : IFoo {}
class Bar : IFoo {}

Foo f = new Foo();
IFoo fInt = f;
Bar b = (Bar)fInt; // Fails.

Casting understands the type hierarchy and the reference of fInt cannot be cast to Bar as it is really Foo. You could provide a user-defined operator to possibly provide this:

public static implicit operator Foo(Bar b) { };

And doing this in your sample code works, but this starts to get silly.

Converting, on the other hand, is completely independent of the type hierarchy. Its behaviour is entirely arbitrary - you code what you want. This is the case you are actually in, converting a Bar to a Foo, you just happen to flag convertible items with IFooCompatible. That interface doesn't make casting legal across disparate implementing classes.


As for why interfaces are not allowed in user-defined conversion operators:

Why can't I use interface with explicit operator?

The short version is that it's disallowed so that the user can be
certain that conversions between reference types and interfaces
succeed if and only if the reference type actually implements that
interface, and that when that conversion takes place that the same
object is actually being referenced.

Implicit conversion not assignable to interface

You're omitting the third option that explains the issue:

//1
TestClass a = "This works fine.";

//2
ITestInterface i = "This doesn't work either!";

//3
DoSomething("Why doesn't this work?");

In (1), you've declared TestClass a. This means that the compiler knows that when you use a different type (string, in this case), that it should try to convert said value to TestClass.

In (2), you've declared ITestInterface i. This means that the compiler knows that when you use a different type (string, in this case), that it should try to convert said value to ITestInterface.

That is the source of the problem. There is no conversion defined between string and ITestInterface.

What you're currently thinking is:

Well, I know that I want this to be converted to a TestClass. Why doesn't the compiler figure out what I want it to do?

The short answer to that is that the compiler refuses to guess.

What you want to happen would lead to impossible situations. For example, what would happen if there was a second class which also implements ITestInterface?

class SecondTestClass: ITestInterface
{
public static implicit operator SecondTestClass(string url)
{
return new SecondTestClass(url);
}

public SecondTestClass(string url)
{
Value = GetValueFromTheInternet(url);
}
public string Value { get; set; }
}

Let's re-evaluate your code:

//1
TestClass a = "This works fine.";

This works. There is a conversion from string to TestClass.

//2
SecondTestClass b = "This works fine.";

This works. There is a conversion from string to SecondTestClass.

//3
ITestInterface i = "This still doesn't work!";

//4
DoSomething("This doesn't work for the same reason as //3");

This doesn't work. The compiler does not have any known conversion from string to ITestInterface.

The compiler is unable to figure out if you want this to be converted to a TestClass and then assigned to i, or if you want this to be converted to a SecondTestClass and then assigned to i.

And, as I said before, the compiler refuses to guess.

Also, just for clarity, this would work:

TestClass a = "This works fine.";

ITestInterface i1 = a;
DoSomething(a);
DoSomething(i1);

SecondTestClass b = "This works fine.";

ITestInterface i2 = b;
DoSomething(b);
DoSomething(i2);

All of these assignations work.

The crux of your problem is that the compiler wants you to explicitly state which type you want the string to be converted to. In your own example, you were already explicitly asking for a TestClass. Notice that this would not have worked if you had used var, as the compiler would not be able to figure it out in that case either.

Why can't I use interface with explicit operator?

Section 10.9.3 of the C# spec spells this out. The short version is that it's disallowed so that the user can be certain that conversions between reference types and interfaces succeed if and only if the reference type actually implements that interface, and that when that conversion takes place that the same object is actually being referenced.

Defining an implicit or explicit conversion between reference types gives the user the expectation that there will be a change in reference; after all, the same reference cannot be both types. On the other hand, the user does not have the same expectation for conversions between reference types and interface types.

User-defined conversions are not allowed to convert from or to interface-types. In particular, this restriction ensures that no user-defined transformations occur when converting to an interface-type, and that a conversion to an interface-type succeeds only if the object being converted actually implements the specified interface-type.



Related Topics



Leave a reply



Submit