Custom conversion operator from the base class

Introduction

I know that "custom conversions to or from the base class are not allowed." MSDN gives "This operator is not needed" as an explanation for this rule.

I understand that a custom conversion to the base class is not required, as this is explicitly done implicitly. However, I need a conversion from the base class.

In my current project, an unmanaged code wrapper, I use a pointer stored in the Entity class. All classes that use a pointer get this Entity class, for example, the Body class.

Therefore I:

Method a

class Entity { IntPtr Pointer; Entity(IntPtr pointer) { this.Pointer = pointer; } } class Body : Entity { Body(IntPtr pointer) : base(pointer) { } explicit operator Body(Entity e) { return new Body(e.Pointer); } } 

This spell is illegal. (Please note that I did not write accessories). Without it, the compiler will allow me to do:

Method B

 (Body)myEntity ... 

However, at runtime, I get an exception saying that this cast is not possible.

Conclusion

So here I need a custom conversion from a base class, and C # fails me. Using method A, the compiler will complain, but the code will logically work at runtime. Using method B, the compiler will not complain, but the code obviously will not work at runtime.

In this situation, it seems strange to me that MSDN tells me that I do not need this operator, and the compiler acts as if it were possible implicitly (method B). What should I do?

I know I can use:

Solution A

 class Body : Entity { Body(IntPtr pointer) : base(pointer) { } static Body FromEntity(Entity e) { return new Body(e.Pointer); } } 

Solution B

 class Body : Entity { Body(IntPtr pointer) : base(pointer) { } Body(Entity e) : base(e.Pointer) { } } 

Solution C

 class Entity { IntPtr Pointer; Entity(IntPtr pointer) { this.Pointer = pointer; } Body ToBody() { return new Body(this.Pointer); } } 

But honestly, all the syntax for them is terrible and in fact should be great. So, any way to make the throws work? Is this a flaw in C # design or am I missing an opportunity? It is as if C # did not trust me enough to write my own transformation from basic to child using my casting system.

+46
casting c #
Aug 03 '10 at 21:56
source share
6 answers

This is not a design flaw. That's why:

 Entity entity = new Body(); Body body = (Body) entity; 

If you were allowed to write your own custom transformation here, there would be two valid conversions: an attempt to simply perform a normal listing (which is an identity-preserving reference transformation) and your custom transformation.

What should i use? Do you really want them to do different things?

 // Reference conversion: preserves identity Object entity = new Body(); Body body = (Body) entity; // User-defined conversion: creates new instance Entity entity = new Body(); Body body = (Body) entity; 

Yuk! So the madness lies, IMO. Remember that the compiler solves this at compile time, based only on the compilation time types of the expressions used.

Personally, I would go with a C solution - and maybe even make it a virtual method. Thus, the Body can override it to simply return this if you want it to be preserved while maintaining the identity, but if necessary create a new object.

+31
Aug 12 '10 at 23:01
source share

Well, when you drop Entity in the Body , you are not really casting to each other, but rather adding IntPtr to the new object.

Why not create an explicit conversion operator from IntPtr ?

 public class Entity { public IntPtr Pointer; public Entity(IntPtr pointer) { this.Pointer = pointer; } } public class Body : Entity { Body(IntPtr pointer) : base(pointer) { } public static explicit operator Body(IntPtr ptr) { return new Body(ptr); } public static void Test() { Entity e = new Entity(new IntPtr()); Body body = (Body)e.Pointer; } } 
+14
Aug 10 '10 at 1:13
source share

You should use your solution B (constructor argument); First, why not use the other proposed solutions:

  • Solution A is just a wrapper for solution B;
  • Solution C is simply wrong (why should the base class know how to transform itself into any subclass?)

In addition, if the Body class must contain additional properties, what should they be initialized when performing your throw? It is much better to use the constructor and initialize the properties of the subclass, as is customary in OO languages.

+9
Aug 04 '10 at 7:54
source share

The reason you cannot do this is because it is unsafe in the general case. Consider the possibilities. If you want to do this because the base and the derived classes are interchangeable, you really only have one class, and you have to combine the two. If you want your casting operator to be convenient for using the downcast base for output, you should consider that not every variable entered as a base class will point to an instance of a specific derived class that you are trying to drop to. Perhaps this is so, but first you will need to check or risk an invalid cast exception. That is why downcasting is generally disapproving, and it is nothing but a drop in resistance. I suggest you reconsider your design.

+2
Aug 10 2018-10-10T00:
source share

What about:

 public class Entity {...} public class Body : Entity { public Body(Entity sourceEntity) { this.Pointer = sourceEntity.Pointer; } } 

therefore, in the code you do not need to write:

 Body someBody = new Body(previouslyUnknownEntity.Pointer); 

but you can use

 Body someBody = new Body(previouslyUnknownEntity); 

instead.

It's just a cosmetic change , I know, but it's pretty clear, and you can easily change the insides. It is also used in a wrapper template, which I cannot remember the name (for minor differences).
It is also clear that you are creating a new object from the one provided, so you should not be misled, as there will be an operator / conversion.

Note: did not use the compiler, therefore there is the possibility of typos.

+1
Aug 15 '10 at 6:23
source share

(Calling the protocols of necromancy ...)

Here is my use case:

 class ParseResult { public static ParseResult Error(string message); public static ParseResult<T> Parsed<T>(T value); public bool IsError { get; } public string ErrorMessage { get; } public IEnumerable<string> WarningMessages { get; } public void AddWarning(string message); } class ParseResult<T> : ParseResult { public static implicit operator ParseResult<T>(ParseResult result); // Fails public T Value { get; } } ... ParseResult<SomeBigLongTypeName> ParseSomeBigLongTypeName() { if (SomethingIsBad) return ParseResult.Error("something is bad"); return ParseResult.Parsed(new SomeBigLongTypeName()); } 

Here Parsed() can infer T from this parameter, but Error cannot, but it can return an impersonal ParseResult , which can be converted to ParseResult<T> - or it would be if it weren't for this error, The fix is ​​to return and convert from subtype:

 class ParseResult { public static ErrorParseResult Error(string message); ... } class ErrorParseResult : ParseResult {} class ParseResult<T> { public static implicit operator ParseResult<T>(ErrorParseResult result); ... } 

And all is happy!

+1
Aug 09 '17 at 4:15 on
source share



All Articles