30

Okay. I've read this post, and I'm confused on how it applies to my example (below).

class Foo
{
    public static implicit operator Foo(IFooCompatible fooLike)
    {
        return fooLike.ToFoo();
    }
}

interface IFooCompatible
{
    Foo ToFoo();
    void FromFoo(Foo foo);
}

class Bar : IFooCompatible
{
    public Foo ToFoo()
    {
        return new Foo();   
    }

    public void FromFoo(Foo foo)
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Bar();
        // should be the same as:
        // var foo = (new Bar()).ToFoo();
    }
}

I have thoroughly read the post I linked to. I have read section 10.10.3 of the C# 4 specification. All of the examples given relate to generics and inheritance, where the above does not.

Can anyone explain why this is not allowed in the context of this example?

Please no posts in the form of "because the specification says so" or that simply quote the specification. Obviously, the specification is insufficient for my understanding, or else I would not have posted this question.

Edit 1:

I understand that it's not allowed because there are rules against it. I am confused as to why it's not allowed.

Community
  • 1
  • 1
gregsdennis
  • 7,218
  • 3
  • 38
  • 71
  • Why did you repost this? – BoltClock Feb 10 '12 at 15:16
  • 1
    It's not clear whether you think that section of the spec doesn't apply to your situation, or whether you're confused about *why* it prohibits this scenario. – Jon Skeet Feb 10 '12 at 15:17
  • Are you aware your sample doesn't compile? Not sure if you know that as part of the question or not... – Adam Houldsworth Feb 10 '12 at 15:20
  • 2
    @BoltClock, I reposted because my question should not have been considered a duplicate and thereby closed. It deserves an answer as this example is quite different than any of the others provided. – gregsdennis Feb 10 '12 at 15:23
  • @AdamHouldsworth, yes, that's why I posted it. – gregsdennis Feb 10 '12 at 15:24
  • 1
    @JonSkeet, I am curious about _why_ it is prohibited, in the context of this example. I understand why that section applies here: it's one of the rules explicitly stated in the spec. – gregsdennis Feb 10 '12 at 15:26

4 Answers4

53

I understand that it's not allowed because there are rules against it. I am confused as to why it's not allowed.

The general rule is: a user defined conversion must not in any way replace a built-in conversion. There are subtle ways that this rule can be violated involving generic types, but you specifically say that you are not interested in generic type scenarios.

You cannot, for example, make a user-defined conversion from MyClass to Object, because there already is an implicit conversion from MyClass to Object. The "built in" conversion will always win, so allowing you to declare a user-defined conversion would be pointless.

Moreover, you cannot even make a user-defined implicit conversion that replaces a built-in explicit conversion. You cannot, for example, make a user-defined implicit conversion from Object to MyClass because there already is a built-in explicit conversion from Object to MyClass. It is simply too confusing to the reader of the code to allow you to arbitrarily reclassify existing explicit conversions as implicit conversions.

This is particularly the case where identity is involved. If I say:

object someObject = new MyClass();
MyClass myclass = (MyClass) someObject;

then I expect that this means "someObject actually is of type MyClass, this is an explicit reference conversion, and now myclass and someObject are reference equal". If you were allowed to say

public static implicit operator MyClass(object o) { return new MyClass(); }

then

object someObject = new MyClass();
MyClass myclass = someObject;

would be legal, and the two objects would not have reference equality, which is bizarre.

Already we have enough rules to disqualify your code, which converts from an interface to an unsealed class type. Consider the following:

class Foo { }
class Foo2 : Foo, IBlah { }
...
IBlah blah = new Foo2();
Foo foo = (Foo) blah;

This works, and one reasonably expects that blah and foo are reference equals because casting a Foo2 to its base type Foo does not change the reference. Now suppose this is legal:

class Foo 
{
    public static implicit operator Foo(IBlah blah) { return new Foo(); }
}

If that is legal then this code is legal:

IBlah blah = new Foo2();
Foo foo = blah;

we have just converted an instance of a derived class to its base class but they are not reference equal. This is bizarre and confusing, and therefore we make it illegal. You simply may not declare such an implicit conversion because it replaces an existing built-in explicit conversion.

So alone, the rule that you must not replace any built-in conversion by any user-defined conversion is sufficient to deny you the ability to create a conversion that takes an interface.

But wait! Suppose Foo is sealed. Then there is no conversion between IBlah and Foo, explicit or implicit, because there cannot possibly by a derived Foo2 that implements IBlah. In this scenario, should we allow a user-defined conversion between Foo and IBlah? Such a user-defined conversion cannot possibly replace any built-in conversion, explicit or implicit.

No. We add an additional rule in section 10.10.3 of the spec that explicitly disallows any user-defined conversion to or from an interface, regardless of whether this replaces or does not replace a built-in conversion.

Why? Because one has the reasonable expectation that when one converts a value to an interface, that you are testing whether the object in question implements the interface, not asking for an entirely different object that implements the interface. In COM terms, converting to an interface is QueryInterface -- "do you implement this interface?" -- and not QueryService -- "can you find me someone who implements this interface?"

Similarly, one has a reasonable expectation that when one converts from an interface, one is asking whether the interface is actually implemented by an object of the given target type, and not asking for an object of the target type that is entirely different from the object that implements the interface.

Thus, it is always illegal to make a user-defined conversion that converts to or from an interface.

However, generics muddy the waters considerably, the spec wording is not very clear, and the C# compiler contains a number of bugs in its implementation. Neither the spec nor the implementation are correct given certain edge cases involving generics, and that presents a difficult problem for me, the implementer. I am actually working with Mads today on clarifying this section of the spec, as I am implementing it in Roslyn next week. I will attempt to do so with as few breaking changes as possible, but a small number may be necessary in order to bring the compiler behaviour and the specification language in line with each other.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 2
    Why did you also forbid implicit conversion operators for interfaces on structs? Because the struct could also implement the interface for which you create the operator? Totally ruins my attempt at creating a struct that functions like an option type in F# ;P – enzi Apr 12 '16 at 13:42
23

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.

Community
  • 1
  • 1
Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • "anyone/everyone can be house under such types" This is precisely what I'm trying to accomplish. I want to say that anyone who implements the `IFooCompatible` interface can be converted to a `Foo` object. Specifically, the object will have implemented the ToFoo() method. – gregsdennis Feb 10 '12 at 15:34
  • @gregsdennis That can be accomplished using mechanisms that aren't banned by the compiler... if that is all you are interested in. I was answering to the question about why the compiler team chose to enforce this on user-defined conversion operators. – Adam Houldsworth Feb 10 '12 at 15:35
  • Maybe what I'm trying to say is that I don't understand Lippert's comments or the specification. – gregsdennis Feb 10 '12 at 15:35
  • Do you think that you could post a modification of my code sample that allows me to do what I'm trying to accomplish? – gregsdennis Feb 10 '12 at 15:36
  • @gregsdennis If I'm being honest, I don't really understand the reasoning also. However I think this is the wrong solution to what you want to achieve. I would favour readability over syntax attractiveness. – Adam Houldsworth Feb 10 '12 at 15:37
  • @gregsdennis I don't need to. You have already achieved the idea of taking any type and having it able to return `Foo` by having it implement an interface. Any code you would write to get you a `Foo` from a type would use this interface and thus you could have just used the interface directly yourself in the first place. Simply: `Foo f = new Bar().ToFoo();` – Adam Houldsworth Feb 10 '12 at 15:39
  • 1
    Yeah, I know I can do that. I already have some implicit conversions for the various classes in my library, and it makes for pretty code. I was hoping that I could implement an interface to allow users of my library to do the same without having to implement their own implicit conversions. – gregsdennis Feb 10 '12 at 15:52
10

Okay, here's an example of why I believe the restriction is here:

class Foo
{
    public static implicit operator Foo(IFooCompatible fooLike)
    {
        return fooLike.ToFoo();
    }
}

class FooChild : Foo, IFooCompatible
{
}

...

Foo foo = new FooChild();
IFooCompatible ifoo = (IFooCompatible) foo;

What should the compiler do here, and what should happen at execution time? foo already refers to an implementation of IFooCompatible, so from that point of view it should just make it a reference conversion - but the compiler doesn't know that's the case, so should it actually just call the implicit conversion?

I suspect the basic logic is: don't allow an operator to defined which could conflict with an already-valid conversion based on the execution-time type. It's nice for there to be exactly zero or one possible conversion from an expression to a target type.

(EDIT: Adam's answer sounds like it's talking about pretty much the same thing - feel free to regard my answer as merely an example of his :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Okay. I can see how that could cause problems, but the point of the interface is to allow an object to be represented as a `Foo`. Since `FooChild` inherits from `Foo`, there's no reason for `FooChild` to implement the interface as a `Foo` can directly hold a `FooChild` without need for conversion. – gregsdennis Feb 10 '12 at 15:45
  • @gregsdennis: What do you mean by "Foo can directly hold a FooChild"? It sounds like you're making assumptions about the point of the inheritance hierarchy here. It may be entirely reasonable for FooChild to derive from Foo, and also to implement IFooCompatible. – Jon Skeet Feb 10 '12 at 15:47
  • I've marked Adam's post as the answer as it ultimately gives the solution/workaround, but I think this is great information as well. Thank you for taking the time. – gregsdennis Feb 10 '12 at 15:54
  • @JonSkeet, point taken. I haven't personally run across an example of when that sort of thing is appropriate, but I won't deny the possibility that it may be. – gregsdennis Feb 10 '12 at 15:56
  • I don't see the ambiguity there. The type of *storage location* `foo` is 'Foo'. Casting a value of *declared* type `Foo` to `IFooCompatible` would require calling the conversion operator, regardless of whether the *instance* implements `IFooCompatible`. The situation would be the same if one was passing `foo` to a member which had overloads for `Foo`, `FooChild`, and `IFooCompatible`. A bigger issue I would think is that classes may implement multiple interfaces, so there could be a number of equally-good conversions available. – supercat Feb 10 '12 at 16:32
  • If `Wowzo` implements `IBiz` and `IBoz`, and conversions exist from both `IBiz` and `IBoz` to `Woozle`, what should happen if one tries to pass a storage location of type `Wowzo` to a routine that expects type `Woozle`? I would suggest that a solution might be to provide that conversion operators would only work either with explicitly-stated interface types, or with generic types that were constrained to interfaces. Not sure if that would introduce other problems, though. – supercat Feb 10 '12 at 16:35
  • @supercat: No, it would *either* require calling the conversion operator *or* it would require performing a normal checked reference conversion. There are two conversions available here, basically - and the most appropriate one to use depends on the execution time type. – Jon Skeet Feb 10 '12 at 17:13
  • @JonSkeet: Except possibly when using "dynamic" types, the compiler doesn't care about the type of an object *instance* when evaluating overloads. If I have a variable of type `Control`, and I call a function which has overloads for `Control` and `Button`, the compiler will use the overload for `Control`, *regardless of whether the variable holds an instance of `Button`*. Even I have a generic parameter constrained to `Control`, and get passed a storage location of type `Button`, the compiler will *still* use the overload for `Control. Why should conversion operators be any different? – supercat Feb 10 '12 at 17:18
  • @supercat: I guess I'd say because the execution path of explicit casts usually *does* take account of the execution-time type. They feel different to me - if I cast a reference which actually refers to an implementation of an interface to that interface type, it would feel odd to me for that to give me back a different reference. – Jon Skeet Feb 10 '12 at 17:23
  • @JonSkeet: I would expect the scenario you describe should be handled in the code for the conversion operator; i.e. `dim asIFC=(fooLike as IFooCompatible); if (asIFC != null) return asIFC;`. IMHO, a big part of the problem is that the same casting operator is used for so many different purposes. In many cases, a cast-to-interface is used in cases where one isn't really interested in having a value of interface type, but simply wants to use an interface method on an existing value. Unfortunately, I don't know any remotely-decent pattern for handling that in the general case. – supercat Feb 10 '12 at 17:31
  • @JonSkeet: For example, suppose one has an object of type `T` and wishes call `IEquatable.Equals` on the object, without boxing, if it implements `IEquatable` and otherwise call `Object.Equals`. If T were statically constrained to `IEquatable`, one could call `IEquatable.Equals` without boxing, but absent such a constraint one can't do so without Reflection. In the specific case of `IEquatable`, there's a specialized static property `EqualityComparer.Default`, but that's a rather clunky workaround. – supercat Feb 10 '12 at 17:39
1

What would probably be helpful here would be for .net to provide a "clean" way to associate an interface with a static type, and have various types of operations on interface types map to corresponding operations on the static type. In some scenarios this can be accomplished with extension methods, but that is both ugly and limited. Associating with interfaces with static classes could offer some significant advantages:

  1. Presently, if an interface wishes to offer consumers multiple overloads of a function, every implementation must implement every overload. Pairing a static class with an interface, and allowing that class to declare methods in the style of extension methods would allow consumers of the class to use overloads provided by the static class as though they were part of the interface, without requiring implementers to provide them. This can be done with extension methods, but it requires that the static method be manually imported on the consumer side.
  2. There are many circumstances where an interface will have some static methods or properties which are very strongly associated with it (e.g. `Enumerable.Empty`). Being able to use the same name for the interface and the 'class' of the associated properties would seem cleaner than having to use separate names for the two purposes.
  3. It would provide a path to supporting optional interface members; if a member exists in an interface but not an implementation, the vtable slot could be bound to a static method. This would be an extremely useful feature, since it would allow interfaces to be extended without breaking existing implementations.

Given that unfortunately such a feature only exists to the extent necessary to work with COM objects, the best alternative approach I can figure would be to define a struct type which holds a single member of interface type, and implements the interface by acting as a proxy. Conversion from the interface to the struct would not require creation of an extra object on the heap, and if functions were to provide overloads which accepted that struct, they could convert back to the interface type in a manner whose net result would be value-preserving and not require boxing. Unfortunately, passing such a struct to a method which used the interface type would entail boxing. One could limit the depth of boxing by having the struct's constructor check whether the interface-type object that was passed to it was a nested instance of that struct, and if so unwrap one layer of boxing. That could be a bit icky, but might be useful in some cases.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • 1
    This is very similar to a design we came up with during the C# 4 design process for a feature called "extension interfaces". It did not make the bar, obviously. Maybe in a hypothetical future version; it would be useful. – Eric Lippert Feb 10 '12 at 17:28
  • @EricLippert: The most essential aspect of the feature would be adding CLR support for #3. That would be more work than supporting it as a compiler-only feature, but would solve one of the present severe limitations of interfaces--the inability to enhance them without breaking existing implementations. For example, one could add `CountingMethod` property to `IEnumerable`, whose default behavior would be to return `CountingMethod.Counted` or `CountingMethod.Enumerate`, based upon how `Enumerable.Count` would work, but which could be overridden by one-time-only or infinite enumerators. – supercat Feb 10 '12 at 17:55
  • @EricLippert: I'm not sure a compiler-only improvement would be worth a whole lot, but being able to correct omissions in existing interfaces would be huge. The corner cases where an interface derived from an existing interface includes a member which gets added to the existing interface could be a little tricky, but I don't think they should pose too much trouble if the derived interface members are assumed to shadow the base type (which would, in the absence of contrary implementation, use the default). – supercat Feb 10 '12 at 17:59