C# DLR, Datatype inference with Dynamic keyword
Asked Answered
N

2

5

Just Asking :

Why 'withOffset' variable is inferred as dynamic as Parse method returns Struct ?

dynamic str = "22/11/2013 10:31:45 +00:01";
var withOffset = DateTimeOffset.Parse(str);

and after explicit cast its back to Struct?

dynamic str = "22/11/2013 10:31:45 +00:01";
var withOffset = DateTimeOffset.Parse((string)str);

since the return type of DateTimeOffset.Parse is DateTimeOffset, and the compiler must know that. keeping that in mind, what ever method it invoke at runtime, the return is always DateTimeOffset.

The specs tells

Since your method takes dynamic as an argument, it qualifies for "dynamic binding"

bit fishy.

What's point in having such specification? OR in which circumstance DateTimeOffset.Parse will not return STRUCT ( forgetting DLR for moment.. )?

Compiler need to be clever, if all methods/overload in the class have same return type to gain performance benefit in long run.

Ninanincompoop answered 22/11, 2013 at 17:16 Comment(6)
Can you expand on "inferred as dynamic"? Can you for example add "withOffset.foo = 4;" and get a good compile?Hexapody
'inferred as dynamic' Compiler thinks its DLR's job to work with this...Ninanincompoop
So in your first example if you added another line "withOffset.foo = 4;" the compiler would not flag this and you would only get an error at runtime.Hexapody
Because that's what the C# Language Specification says. Chapter 7.6.5Dramatization
@Hans, I understood your thought, But question is not about validating spec with implementation, but rather to understand Why its like that?Ninanincompoop
Because making predictions what method is actually going to bind at runtime is dangerous.Dramatization
T
4

When you use dynamic, the entire expression is treated at compile time as a dynamic expression, which causes the compiler to treat everything as dynamic and get run-time binding.

This is explained in 7.2 of the C# Language specification:

When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of constituent expressions are used in the selection process. However, when one of the constituent expressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound.

This basically means that most operations (the types are listed in section 7.2 of the spec) which have any element that is declared as dynamic will be evaluated as dynamic, and the result will be a dynamic.


this is because in below line str is dynamic

         dynamic str = "22/11/2013 10:31:45 +00:01";
        var withOffset = DateTimeOffset.Parse(str);

At compile time str is dynamic, the type of str get to know at runtime only that is the reason compiler treat withOffset as dynamic


its known to you that str is get converted to datetime structure but for compiler that it get to know only at runtime...

Tweeze answered 22/11, 2013 at 17:21 Comment(7)
Nope, We already know that DateTimeOffset.Parse is always returns STRUCT (DateTimeOffset), Then Why var has infered as Dynamic?Ninanincompoop
because as i told str is dynamic and var is get converted to the datatype which is get assinged to it, i say tis purly liggel to say var is going to be dynamic in this caseTweeze
Pranay, as we know the return type is Struct in advance, Whats point in having such RUDE specification? OR will ever DateTimeOffset.Parse suppose to return 'Dynamic'?Ninanincompoop
@DipakBava: Since the argument is dynamic, the compiler cannot know which method will be called at runtime. Therefore, it cannot know what the return type will be. We might know that the return type will be DateTimeOffset, but the compiler does not, and cannot, know that.Droop
@Richard, You have point as per Spec, Does that mean if we have only one method 'public int Foo(int i)' in class 'Bar', still Why compiler need to return dynamic as there is no other overload for Foo and the Type 'Bar' is well known in advance.Ninanincompoop
@DipakBava: Because looking up every possible overload of every possible method that might be invoked by a dynamic call just to see if they all return the same value would result in a dramatic increase in compilation time and compiler complexity for very little gain.Droop
Though, method return type has no role in deciding overload.Ninanincompoop
Y
1

Here's a really good example of where your assumptions about return type begin to go awry.

public class Bar
{
    public string Foo(string value)
    {
        return value;
    }
}

As you can see here, Bar clearly has an instance method Foo that takes a string and returns a string.

public class Baz : Bar, IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression parameter)
    {
        return new BazMetaObject(parameter, this);
    }
}

But now I've created a derived class that also implements IDynamicMetaObjectProvider. This is the interface that C# uses to get a DynamicMetaObject, which determines how the dynamic calls are bound at runtime. For example:

public class BazMetaObject : DynamicMetaObject
{
    public BazMetaObject(Expression expression, Baz value)
        : base(expression, BindingRestrictions.Empty, value)
    {
    }

    public override DynamicMetaObject BindInvokeMember(
        InvokeMemberBinder binder, DynamicMetaObject[] args)
    {
        if (binder.Name == "Foo")
        {
            return new DynamicMetaObject(
                Expression.Convert(
                    Expression.Call(
                        typeof (BazMetaObject).GetMethod("DynamicFoo")
                    ),
                    typeof (object)
                ),
                BindingRestrictions.GetTypeRestriction(
                    this.Expression,
                    this.LimitType
                )
            );
        }

        return base.BindInvokeMember(binder, args);
    }

    public static int DynamicFoo()
    {
        return 1234;
    }
}

This DynamicMetaObject overload will capture any calls to Foo and dynamically redirect them to DynamicFoo, which has a completely different signature—including that it returns int, not string.

So, if you were to do this...

dynamic value = "Hello, world!";

Bar bar = new Baz();
var returnValue = bar.Foo(value);

...the value of returnValue at runtime would be 1234, not "Hello, world!".

Now, in the real world, this is insanely evil. While it's possible to completely rebind functions that are expected to do something in a certain way, doing something ridiculous like this will only serve to confuse people down the road. That being said, it is completely possible in the CLR.

TL;DR: When you're using dynamic, you can't always be certain of things you think are correct.

Yttrium answered 22/11, 2013 at 21:47 Comment(2)
Sorry, You can intercept the call using BindInvokeMember by all means. However I feel you have got OOP concept wrong in terms of inheritance chain. Why Base class/interface need to know about its derived class implementation of method/prop, and if base class has to know (little) then the method/prop is abstract/virtual, hence, method signature remain same.Ninanincompoop
So in this case bar.Foo(value) means intention is to call Foo method with signature string Foo(dynamic valu e). I.e. Baz is derived from Bar, means Bar really know nothing about Baz unless Bar makes Foo() as virtual (but return type remain same  ). Apart from that whatever type dynamic resolve at runtime, the return type of method will remain same unless there is method group with different return type within same/base class. Your example is to compensate dynamic at cost of OOPsNinanincompoop

© 2022 - 2024 — McMap. All rights reserved.