简体   繁体   中英

Represent the type Func<dynamic, object> via reflection

Given this method signature:

void Foo<T>(Func<T, object> expression)

Is it possible to use reflection to create a type-representation of Func<dynamic, object> to use with MakeGenericType for the expression argument's type? The "obvious" approaches are not valid C# syntax because dynamic is neither a type nor an object (ie. typeof(dynamic) is invalid), so I've not been able to come up with anything useful for the ??? parameter below:

Type fnType = typeof(Func<,>).MakeGenericType(new Type[] { ???, typeof(object) });

Interestingly, I can do this and it compiles without errors, but the script compiler throws at runtime, I think because that typeof is really returning a Func<object, object> :

Type fn = typeof(Func<dynamic, object>);

At least, anything I can find through reflection or the debugger seems indistinguishable from typeof(Func<object, object>) . Of course, I realize dynamic is a special case in the C# language -- behind-the-scenes black-box "magic" behavior somehow attached to an object . The question, I suppose, is what makes that object special when I write something like this:

Foo<dynamic>(n => new { n.prop });

Since dynamic tends to generate a flurry of "your architecture sucks" replies, I'll preempt those by explaining the real-world scenario: I'm using the Roslyn Scripting API to load and compile expression delegates from configuration to filter, destructure, or otherwise alter various objects (including anonymous types, hence dynamic ) written to a structured logger (Serilog).

I am starting to think this is an edge-case that reflection just can't handle. (I've been hoping to avoid Expressions, but I'm wondering if that could pull it off somehow.)

Edit: Real Code

Sample inputs (that actually work) might be Sample.Account (a class in my test console program) and a => new { a.Username } as the transformation expression to compile, demonstrating a common structured-logging example of an account class storing a username and password, and you use destructuring to strip the password. (I've already populated ScriptingOptions with the necessary assembly references and imports before this is called.)

The output from this (using the inputs described above) would be an instance of Func<Sample.Account, object> . The question is how to do this sort of thing to obtain Func<dynamic, object> as the output (which can be written and compiled as source, but as far as I can tell, can't be set up through reflection).

private static dynamic CompileTransformation(string transformedType, string transformation)
{
    // get a Type that corresponds to namespace.type in transformedType
    Type TValue = Type.GetType(transformedType) ??
        AppDomain.CurrentDomain.GetAssemblies()
        .Select(a => a.GetType(transformedType))
        .FirstOrDefault(t => t != null);

    // get a representation of Func<TValue, object>
    Type funcType = typeof(Func<,>).MakeGenericType(new Type[] { TValue, typeof(object) });

    // get a representation of CSharpScript.EvaluateAsync<Func<TValue, object>>()
    var evalMethod = typeof(CSharpScript).GetMethods()
        .FirstOrDefault(m => m.Name.Equals("EvaluateAsync") && m.IsGenericMethod)
        .MakeGenericMethod(funcType);

    // execute EvaluateAsync
    dynamic evalTask = evalMethod.Invoke(null, new object[] { transformation, ReflectionHelper.scriptOptions, null, null, null });
    dynamic compiledFunc = evalTask.GetAwaiter().GetResult();

    return compiledFunc;
}

It's not possible to do that via reflection, because dynamic concept only exists at compile time, not a run time.

If you compile something like:

dynamic x = "test";
Console.WriteLine(x.Length);

And decompile result in something like DotPeek - you'll see quite a bunch of cryptic, reflection-like code to which compiler has converted your dynamic code. And x is really of type object , so the whole thing is basically similar to typeof(string).GetProperty("Length").GetValue(x) , but maybe more effective. But nowhere you will see any trace of dynamic itself.

So, there is no way to somehow obtain Func<dynamic, object> from Func<T, object> at runtime.

Foo<dynamic>(n => new { n.prop });

Is conceptually similar to something like:

Foo<object>((object n) => new { prop = n.GetType().GetProperty("prop").GetValue(n) });

I've unfortunately didn't quite understood your use case, though I tried, so cannot give reasonable advice about what to use instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM