Представляет тип Func <dynamic, object> через отражение - PullRequest
0 голосов
/ 04 мая 2018

С учетом подписи этого метода:

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

Можно ли использовать отражение, чтобы создать представление типа Func<dynamic, object> для использования с MakeGenericType для типа аргумента expression? «Очевидные» подходы недопустимы в синтаксисе C #, поскольку dynamic не является ни типом, ни объектом (т. Е. typeof(dynamic) недопустим), поэтому я не смог придумать ничего полезного для параметра ??? ниже:

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

Интересно, что я могу сделать это, и он компилируется без ошибок, но компилятор сценариев выдает во время выполнения, я думаю, потому что typeof действительно возвращает Func<object, object>:

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

По крайней мере, все, что я могу найти с помощью отражения или отладчика, кажется неотличимым от typeof(Func<object, object>). Конечно, я понимаю, что dynamic - это особый случай на языке C # - закулисное «магическое» поведение черного ящика, каким-то образом привязанное к object. Вопрос, я полагаю, в том, что делает это object особенным, когда я пишу что-то вроде этого:

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

Поскольку dynamic имеет тенденцию генерировать поток ответов "ваша архитектура отстой", я опережу их, объясняя реальный сценарий: я использую API сценариев Roslyn для загрузки и компиляции делегатов выражений из конфигурации в фильтровать, деструктурировать или иным образом изменять различные объекты (включая анонимные типы, следовательно, dynamic), записанные в структурированный регистратор (Serilog).

Я начинаю думать, что это крайний случай, с которым отражение не может справиться. (Я надеялся избежать выражений, но мне интересно, может ли это как-то справиться.)

Редактировать: реальный код

Примеры входных данных (которые действительно работают) могут быть Sample.Account (класс в моей тестовой консольной программе) и a => new { a.Username } в качестве выражения преобразования для компиляции, демонстрируя общий пример структурированной регистрации класса учетной записи, хранящей имя пользователя и пароль, и вы используете деструктуризацию, чтобы удалить пароль. (Я уже заполнил ScriptingOptions необходимыми ссылками на сборки и импортом до того, как это будет вызвано.)

Выход из этого (с использованием входов, описанных выше) будет экземпляром Func<Sample.Account, object>. Вопрос в том, как это сделать, чтобы получить Func<dynamic, object> как вывод (который может быть записан и скомпилирован как источник, но, насколько я могу судить, не может быть настроен с помощью отражения).

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;
}

1 Ответ

0 голосов
/ 04 мая 2018

Это невозможно сделать с помощью отражения, поскольку концепция dynamic существует только во время компиляции, а не во время выполнения.

Если вы скомпилируете что-то вроде:

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

И декомпиляция приводит к чему-то вроде DotPeek - вы увидите довольно много загадочного, похожего на отражение кода, в который компилятор преобразовал ваш dynamic код. И x действительно имеет тип object, так что все это в основном похоже на typeof(string).GetProperty("Length").GetValue(x), но, возможно, более эффективно. Но нигде вы не увидите никаких следов dynamic.

Итак, нет способа каким-либо образом получить Func<dynamic, object> из Func<T, object> во время выполнения.

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

Концептуально похож на что-то вроде:

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

К сожалению, я не совсем понял ваш вариант использования, хотя и пытался, поэтому не могу дать разумный совет о том, что использовать вместо него.

...