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

Good. I read this post and I am confused about how this 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 carefully read the message I contacted. I read section 10.10.3 of the C # 4 specification. All of the examples given refer to generics and inheritance, where this is not indicated.

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

Please, no messages in the form “because the specification says so” or simply quotes the specification. Obviously, the specification is not enough for my understanding, otherwise I would not have published this question.

Change 1:

I understand that this is forbidden because there are rules against him. I am confused why this is not allowed.

+20
c # interface implicit-conversion
Feb 10 2018-12-12T00:
source share
4 answers

The context of your example, it will not work again, because the implicit operator was placed against the interface ... I'm not sure how you think your sample is different from the one you are associated with, except that you are trying to get one specific type through another through the interface.

The topic is discussed here:

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

And Eric Lippert could explain the reason when he said in your related question:

Casting an interface value is always considered a type test, because it is almost always possible that an object really has this type and really implements this interface. We do not want to deny you the opportunity to make a cheap transformation that preserves the view.

It seems that refers to the type. Specific types are linked to each other through their hierarchy, so type identification can be applied through it. With interfaces (and other blocked things like dynamic and object ), the type identifier becomes controversial, because any / everyone can be placed under such types.

Why this is important, I have no idea.

I prefer explicit code that shows me. I am trying to get Foo from another, that is, IFooCompatible , so the conversion procedure that returns T where T : IFooCompatible Foo T where T : IFooCompatible .

For your question, I understand the point of discussion, however, my bold answer is that if I see code like Foo f = new Bar() in the wild, I would most likely reorganize it.




Alternative solution:

Not over egg pudding here:

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

You already realized that Foo compatible types implement an interface to ensure compatibility, use this in your code.




Casting vs. Conversion:

It is also easy to get wires crossed relative to casting and conversion. Casting implies that type information is integral between the types you throw, so casting does not 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 fInt link cannot be cast to Bar , because it is really Foo . You could provide a custom operator to provide this:

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

And doing this in your code example works, but it starts to get dumb.

Conversion, on the other hand, is completely independent of the type hierarchy. Its behavior is completely arbitrary - you encode what you want. This is the case when you are actually converting Bar to Foo , you just discover convertible elements with IFooCompatible . This interface does not allow casting to be legal through disparate implementation classes.




As for why the interfaces are not allowed in custom conversion operators:

Why can't I use an interface with an explicit statement?

The short option is that it is forbidden, so that the user can succeed if the conversions between reference types and interfaces succeed if and only if the reference type actually implements this interface, and that when this conversion happens, the same object actually refers .

+22
Feb 10 2018-12-12T00:
source share

I understand that this is forbidden because there are rules against him. I am confused why this is not allowed.

General rule: a user transformation should in no way replace an inline transformation. There are subtle ways that this rule can be broken using common types, but you specifically say that you are not interested in scripts of a typical type.

You cannot, for example, do a custom conversion from MyClass to Object , because there is already an implicit conversion from MyClass to Object . An inline transform will always win, so you can declare that a custom transform will be meaningless.

In addition, you cannot even do a custom implicit conversion that replaces the built-in explicit conversion. You cannot, for example, do an implicit user conversion from Object to MyClass , because there is already a built-in explicit conversion from Object to MyClass . This is too confusing for the code reader to allow you to arbitrarily reclassify existing explicit conversions as implicit conversions.

This is especially true of personality. If I say:

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

then I expect this to mean that " someObject really is of type MyClass , this is an explicit reference conversion, and now MyClass and someObject are reference equals." If you were allowed to say

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

then

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

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

We already have enough rules to disqualify code that is converted 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 it is reasonable to expect that blah and foo are reference equal, because casting Foo2 to its base type Foo does not change the link. Now suppose this is legal:

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

If this is legal, then this code is legal:

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

we just converted an instance of a derived class to its base class, but they are not referenced. This is weird and confusing, so we make it illegal. You simply cannot declare such an implicit conversion because it replaces the existing built-in explicit conversion.

Thus, the rule that you should not replace any inline transformation with any custom transformation is sufficient to deny the possibility of creating a transformation that accepts an interface.

But wait! Suppose foo sealed. Then there is no conversion between IBlah and foo , explicit or implicit, because the derivative Foo2 that implements IBlah cannot be obtained. In this case, should we allow custom conversion between foo and IBlah ? Such a custom conversion cannot replace any built-in conversion, explicit or implicit.

No. We have added an additional rule to section 10.10.3 of the specification, which explicitly prohibits any user transformation to or from an interface, regardless of whether or not the inline transformation replaces or does not replace.

Why? Since you have a reasonable expectation that when one converts a value to an interface, you check to see if this object implements the interface without requesting a completely different object that implements the interface. In COM terms, converting to the QueryInterface interface is "Do you implement this interface?" - and not QueryService - "can you find me for those who implement this interface?"

Similarly, there is a reasonable expectation that when one is converted from an interface, one asks if the interface is really implemented by an object of a given target type and does not require that the object of the target type be completely different from the object that implements the interface.

Thus, it is always forbidden to do a custom conversion that is converted to or from an interface.

However, generics are highly polluting waters, the wording of the specification is not very clear, and the C # compiler contains a number of errors in its implementation . Neither the specification nor the implementation is correct, given some cross-cases related to generics, and this is a difficult problem for me, the executor . I actually work with Mads today to clarify this section of the specification, as I will be implementing it at Roslyn next week. I will try to do this with as few changes as possible, but a small number may be required to bring the compiler behavior and specification language into line with each other.

+37
Feb 10 2018-12-12T00:
source share

Ok, here is an example of why I find the limitation 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 runtime? foo already refers to the implementation of IFooCompatible , so from this point of view it should just make it a reference conversion - but the compiler does not know this case, so should it really just call an implicit conversion?

I suspect that the basic logic is: not to allow an operator that may contradict an existing rule based on the type of runtime. It’s good that there should be exactly zero or one possible conversion from the expression to the target type.

(EDIT: Adam’s answer sounds like he’s saying the same thing - feel free to take my answer as just an example of it :)

+9
Feb 10 '12 at 15:33
source share

Which might be useful here for .NET to provide a “clean” way to associate an interface with a static type and use various types of operations on interface types for the corresponding operations on the static type. In some scenarios, this can be accomplished using extension methods, but it is both ugly and limited. Binding interfaces to static classes can have some significant advantages:

  • Currently, if an interface wants to offer consumers several function overloads, each implementation must implement each overload. The combination of a static class with an interface and allowing this class to declare methods in the style of extension methods will allow class consumers to use the overloads provided by the static class as if they were part of the interface without requiring executors to provide them. This can be done using extension methods, but for this requires that the static method be imported manually on the consumer side.
  • There are many circumstances when an interface will have some static methods or properties that are very strongly related to it (for example, `Enumerable.Empty`). The ability to use the same name for an interface and the “class” of related properties will seem cleaner than using separate names for two purposes.
  • This will provide a way to support optional interface members; if an element exists in the interface but not in the implementation, the vtable slot can be bound to a static method. This would be a useful feature of extreme , as it would allow you to extend interfaces without breaking existing implementations.

Given that, unfortunately, such a function exists only to the extent that it is necessary to work with COM objects, the best alternative approach I can understand would be to determine the type of structure, which contains one member of the interface type, and implements the interface acting as a proxy. Converting from an interface to a structure does not require the creation of an additional object on the heap, and if functions were supposed to provide overloads that accepted this structure, they could be converted back to the type of interface in a way that would save the values ​​and do not require a box. Unfortunately, passing such a structure to a method that uses an interface type will entail boxing. You can limit the depth of the box if the constructor of the structure checks whether the object of the interface of the type that was passed to it is a nested instance of this structure and, if so, expand one layer of the box. This may be a little unpleasant, but may be useful in some cases.

+1
Feb 10 '12 at 15:56
source share



All Articles