Why Can't I Use Interface with Explicit Operator

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.

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.

explicit interface implementation, why explicit casting

When a class explicitly implements an interface why do you need to explicitly cast the class instance to interface in order to use implemented method?

The member effectively doesn't exist on the class, as far as the compiler's concerned - it only exists on the interface. You don't have to explicitly cast though - you just have to have a reference which has a compile-time type of the interface. That can be done however you like, including implicit conversions.

In the above code block, why do you have

IControl c = (IControl)obj;

as opposed to

IControl c = obj;

You don't have to. The implicit conversion should be absolutely fine. You would have to cast explicitly in order to call the method in a single expression, e.g.

obj.Paint(); // Invalid
((IControl) obj).Paint(); // Valid

But if you go through an implicit conversion via assignment to a separate local variable of the interface type, that's fine - the method is still being called with a target expression which is of the interface type.

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 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.

Why isn't my cast operator from T to SomeStructT working when T is an interface?

Well, the system can't do an actual cast because S<J> does not implement J. It works fine if you change your declaration:

public struct S<T> : J {...}

In the case of classes, it's possible for a sub-type of a class to be declared which does implement J. But structs cannot be extended, so if S doesn't implement J then you can guarantee that no class that "is a" S will ever implement J either. Therefore no J will ever be an S<T>.

So why doesn't it invoke your explicit cast operator? I think the answer is in the C# spec, as described in this SO post.


6.4.1 Permitted user-defined conversions

C# permits only certain user-defined conversions to be declared. In particular, it is not possible to redefine an already existing implicit or explicit conversion.
For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S0 and T0 are different types.
  • Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • Neither S0 nor T0 is an interface-type.
  • Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

So if I'm reading that right, because J is an interface, it is not recognized as a candidate for your explicit cast operator.

Why doesn't C# allow types that use composition to have implicit conversions for interfaces?

This is detailed in section 10.10.3 of the C# language spec.

The summary reason of why though is ...

  • Conversion operators should not replace built-in conversions. Allowing this just leads to extremely confusing behavior
  • In general it's not possible to determine if an implicit conversion to an interface is replacing a built-in conversion and hence it's disallowed


Related Topics



Leave a reply



Submit