12

Given this class with an implicit cast operator:

public class MyDateTime
{
    public static implicit operator MyDateTime(System.Int64 encoded)
    {
        return new MyDateTime(encoded);
    }

    public MyDateTime(System.Int64 encoded)
    {
        _encoded = encoded;
    }
    System.Int64 _encoded;
}

I can now do the following:

long a = 5;
MyDateTime b = a;

But NOT the following:

long f = 5;
object g = f;
MyDateTime h = g;

This gives a compile time:

Cannot implicitly convert type 'object' to 'MyDateTime'.

Makes sense to me.

Now I modify the previous example as follows:

long f = 5;
object g = f;
MyDateTime h = (MyDateTime)g;

This compiles fine. Now I get a runtime InvalidCastException:

Unable to cast object of type 'System.Int64' to type MyDateTime'.

This tells me that C# implicit cast operators are applied at compile time only, and are not applied when the .NET runtime is attempting to dynamically cast an object to another type.

My questions:

  1. Am I correct?
  2. Is there some other way to do this?

By the way, the full application is that I'm using Delegate.DynamicInvoke() to call a function that takes a MyDateTime parameter, and the type of the argument I'm passing to DynamicInvoke is a long.

YOU
  • 120,166
  • 34
  • 186
  • 219
Eric
  • 2,029
  • 2
  • 26
  • 36

3 Answers3

14

Am I correct?

Yes, yes you are. To be nit-picky, you should be saying "user-defined implicit conversion" rather than "implicit cast" -- a cast is (almost) always explicit. But your deduction that overload resolution chooses which user-defined conversion to call at compile time and not at run time is correct.

Is there some other way to do this?

Yes. In C# 4 if you type your "object" as "dynamic" then we start up the compiler again at runtime and re-perform all the analysis on the operands as though their compile-time types were the current run-time types. As you might imagine, this is not cheap, though we are very smart about caching and re-using the results should you do this in a tight loop.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Just out of curiosity, what are the reasons that operators don't behave more like methods at runtime? Given your answer, it feels as if they are mere syntactic sugar and not much more. In my view it would make C# even more powerful if these kind of operators are promoted as a first-class member of any type (which means they become part of all the other object-oriented goodness like inheritance, overriding and interfacing). Would leveraging these at runtime by default imply major changes to the design and implementation of the language? – MarioDS Feb 19 '16 at 16:18
  • 1
    @MDeSchaepmeester: You are right that there is a bit of a disconnect here. Compare the design of int to decimal for example. You can say `Func add = decimal.Add;` but there is no way to do the same for int; you have to say `Func add = (x, y) => x + y`. It would have been nice I think had all the built-in types been designed as Decimal was, and then the runtime or compiler could choose to lower them to more fundamental operations for performance reasons. But this sort of consistency really follows from a "functional" mindset. – Eric Lippert Feb 19 '16 at 16:38
  • 1
    @MDeSchaepmeester: I suspect that the original designers of the runtime simply were not thinking about that sort of unifying functional abstraction when designing the type system and the operations on built-in types. And of course it is also odd that decimal has both an operator+ and an Add method. And don't even get me started on the dozen ways there are to represent the equality operation! It's a bit of a mess really. Next time you design a framework from scratch, get this stuff right early. – Eric Lippert Feb 19 '16 at 16:39
  • 1
    @MDeSchaepmeester: Also as you note none of this stuff works well with interfaces. An often-proposed feature is to add "static interfaces" so that you could say `Matrix where T : IAddable` and the like. That really would require some pretty serious work on the language and the runtime, so it's never been super high on the priority list. – Eric Lippert Feb 19 '16 at 16:42
  • @EricLippert: Jon Skeet's [MiscUtil](http://www.yoda.arachsys.com/csharp/miscutil/) provides generic operators (e.g., `Operator.Add` and `Operator.Add`) in a relatively clean way. This gets you generic operators functions, but doesn't give you type safety, which is more important. However, you might be able to get similar protection via [Code Contracts](https://msdn.microsoft.com/en-us/library/dd264808.aspx) – Brian Feb 19 '16 at 18:29
  • Can I somehow use the dynamic implicit conversion in Expression Lambda? I have something like `Expression.Lambda(delegateType, Expression.Convert(getValExpr, resType), exprParameters)` but the `processCallExpr` results and `object`. Example: type from the processCall is `int`, resType type is `double` and I need some expression to convert it implicitly instead of cast error. – mvorisek Mar 23 '19 at 22:52
-3

I know this is an older question but in case anyone else stumbles upon the same problem, this will compile and run fine:

long f = 5;
object g = f;
MyDateTime h = g as MyDateTime;
Roman Reiner
  • 1,089
  • 1
  • 10
  • 17
  • It will run fine in the sense that it will not throw an `InvalidCastException`, however `h` will be `null` which is not what the OP wants or expects. – MarioDS Feb 19 '16 at 16:20
-4

Adding an explicit operator should work: http://msdn.microsoft.com/en-us/library/85w54y0a(VS.80).aspx

Pete
  • 11,313
  • 4
  • 43
  • 54