Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# CallerMemberName not working with dynamic in args?

Tags:

c#

dynamic

I have a method that accepts a dynamic argument, and also has [CallerMemberName] on a different argument. The [CallerMemberName] argument doesn't get populated as long as the dynamic object is also in the arguments.

Is this a bug in the CallerMemberName or in C# itself? See below for a test

    static void Main(string[] args)
    {
        new CMNTest().BaseMethod();
    }

    public void BaseMethod()
    {
        NormalMethod();
        dynamic myDyn = new Object();
        DynamicArgumentMethod(myDyn);
        ObjectArgumentMethod(new object());
        ObjectArgumentMethod((object)myDyn);
    }

    public void NormalMethod([CallerMemberName]string methodName = null)
    {
        Console.WriteLine(methodName);
    }

    public void DynamicArgumentMethod(dynamic dynObject, [CallerMemberName]string methodName = null)
    {
        Console.WriteLine(methodName);
    }

    public void ObjectArgumentMethod(object otherArg, [CallerMemberName]string methodName = null)
    {
        Console.WriteLine(methodName);
    }

Output:

BaseMethod
<null>
BaseMethod
BaseMethod

Tested in Visual Studio and LinqPad, same results. Any way around this? I need to do dynamic things with the object, so casting into an object won't work.

like image 457
DLeh Avatar asked Dec 07 '22 01:12

DLeh


1 Answers

Is this a bug in the CallerMemberName or in C# itself?

No; the supposition that there is a bug at all is the error.

C# CallerMemberName not working with dynamic in args?

That's correct. That's by design. It's not a bug.

How does CallerMemberName work?

CallerMemberName works using the same mechanisms as optional arguments; if you don't understand those mechanisms then step one is to understand them. See my articles on the subject:

http://ericlippert.com/tag/optional-arguments/

Read those before you continue on.

.....

OK, now that you understand that optional arguments are constants that are inserted by the compiler at the call site, it should now be easier to understand how the caller attributes work. They are optional arguments which are replaced with the "constant" that is the location of the call site.

Now think about how "dynamic" works. "Dynamic" means "abandon analysis at compile time and start the compiler again at runtime; determine what the compiler would have done had it known the runtime types at compile time, and generate fresh code just like the compiler would have generated". How does the compiler know to persist the current location as a constant into the call site? The fact that it needs to do so is discovered by compile-time overload resolution, which you just turned off.

The compiler which runs at runtime does not have information about where in the source code the original call was; that information is not persisted into the call site because the original compile-time compiler has no reason to believe it needs to do so.

This is incidentally the same reason why extension methods cannot be dispatched dynamically: the call site payload does not include all the static classes that were "in scope" at the point of the call.

Unfortunately this is just one of the prices that you pay for fully dynamic dispatch.

like image 192
Eric Lippert Avatar answered Dec 10 '22 12:12

Eric Lippert