8

Say, I have an interface

public interface ISomeControl
{
    Control MyControl { get; }
    ...
}

Is it possible to define something like this:

public static implicit operator Control(ISomeControl ctrl)
{
    return ctrl.MyControl;
}

Or rather why can't I do that in C#?

Michiel van Oosterhout
  • 22,839
  • 15
  • 90
  • 132
horgh
  • 17,918
  • 22
  • 68
  • 123
  • 1
    @leppie What am I missing? Why is this approach absolutely wrong? – horgh Sep 21 '12 at 15:43
  • For starters an interface cannot have any implementation so you have nowhere to define that operator – Jamiec Sep 21 '12 at 15:44
  • 1
    I have no idea why :) You will to summon @ericlippert for that answer :) – leppie Sep 21 '12 at 15:45
  • IIRC, the CLR will allow to make such code, just not C#. – leppie Sep 21 '12 at 15:51
  • possible duplicate of [Why can't I use interface with explicit operator?](http://stackoverflow.com/questions/2433204/why-cant-i-use-interface-with-explicit-operator) – nawfal Apr 20 '13 at 11:58

2 Answers2

6

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.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I can understand that allowing conversions from an interface to a class type would be ambiguous, but I don't see the ambiguity if a class defines a conversion operator from an interface to itself. If there exists a conversion from `BaseType` to `Foo`, a class `DerivedType` may also define a conversion to `Foo` without creating ambiguity. If a class doesn't implement a particular interface but a subclass does, casting a base-type reference to that interface should use the user-defined conversion (regardless of whether the referenced object implements the interface)... – supercat Sep 21 '12 at 18:30
  • ...while using a derived-class reference should use the conversion which is statically defined for the derived class. Even in the case of `Foo : ISomething` specifying a conversion to `ISomething` I don't see interfaces as posing any "problem" that classes wouldn't. – supercat Sep 21 '12 at 18:33
  • @supercat: `Foo : ISomething` isn't allowed to define a conversion to `ISomething` (per the rules quoted by Roland). And the rationale is that if `T` is `Int32` there would be conflicting conversions. And no, casting a base class can't use the user-defined conversion, because a base class variable may hold a handle to a derived class instance that implements the interface, and it is required that casting an instance to an interface it implements always uses the built-in (cross-cast) pointer adjustment (it's not a conversion, it doesn't create a new object). – Ben Voigt Sep 22 '12 at 02:52
  • 3
    @BenVoigt: How is that situation different from `Derived : Base` with conversion operator from `Derived` to `Base`? The conversion operator is applied when trying to store to a `Base` something that the compiler can tell is a type `Derived` for some T, but can't tell that it's a `Derived`. The idea that a cast to an interface is reference-preserving may happen to be true if the thing being cast is a reference type because of the lack of any other way to perform the cast, but a cast of a value type to an interface type is not... – supercat Sep 22 '12 at 15:34
  • 2
    ...reference-preserving, and the cast of an unconstrained generic to a reference type may or may not be reference-preserving. Actually, there are many situations with generics where a user-defined conversion operator may be invoked to cast an object to what happens to be its own type, without complaint. The rules of what things are, or are not, ambiguous are weird. I find it strange, for example, that if Foo defines a widening conversion to Bar, and Bar defines a widening conversion from Foo, neither conversion will work because of "ambiguity". Why not declare that "conversion from" wins? – supercat Sep 22 '12 at 16:02
  • @supercat: Why declare that "conversion from" wins? That's likely to confuse approximately half of programmers who write code with ambiguous conversions. When something is that easy to fix, a warning or error is the best approach, because it lets the developer control the behavior, and prevents silent misbehavior. – Ben Voigt Sep 23 '12 at 13:29
  • @BenVoigt: In most cases, the implementations of the conversions would be identical, and one could strongly advise against writing code which relied upon the compiler to favor a particular one. The reason I would favor "conversion from" is that if one derives a new class and allow other things to be converted to it, it would generally make more sense to put all the required conversion operators in the new type (as "conversions from" other types), than to add code to each type from which the thing could be converted. "Conversion to" is generally a fallback when conversion from isn't workable. – supercat Sep 23 '12 at 18:54
  • 1
    @supercat: You talk about a "new type", but in order for conversions to exist in both types and each be aware of the other type, it's hard to say one type is newer than the other. – Ben Voigt Sep 23 '12 at 19:25
2

You cannot do that.

C# specification says:

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.

One way you can do it is to have a static method.

public class Control
    {
        public static Control FromISomeControl(ISomeControl ctrl)
        {
            return ctrl.MyControl;
        }
    }
Roland
  • 972
  • 7
  • 15