(Действие <T>). Имя не возвращает ожидаемые значения - PullRequest
1 голос
/ 06 апреля 2010

У меня есть следующий метод (используется для создания дружественных сообщений об ошибках в модульных тестах):

protected string MethodName<TTestedType>(Action<TTestedType> call)
{
    return string.Format("{0}.{1}", typeof(TTestedType).FullName, call.Method.Name);
}

Но когда я называю это следующим образом, я не получаю ожидаемых результатов:

var nm = MethodName<MyController>(ctrl => ctrl.Create());

После запуска этого кода nm содержит "<Create_CreateShowsView>b__8", а не (как ожидалось) "Create". Как мне изменить код, чтобы получить ожидаемый результат?

Ответы [ 2 ]

7 голосов
/ 06 апреля 2010

Вам нужно передать Expression вместо Action. На самом деле не так сложно использовать деревья выражений для этого, когда вы понимаете, как выглядит дерево.

Строка кода:

(MyClass c) => c.TestMethod();

Может быть разбит как лямбда-выражение (весь блок), содержащее один параметр (c, слева) и тело (c.TestMethod(), справа).

Тогда «тело» - это вызов метода для конкретного объекта (который является параметром c), фактического метода (TestMethod) и набора аргументов (в этом случае нет есть).

Визуально:

            LambdaExpression   [ (MyClass c) => c.TestMethod() ]
             /           \
            /             \
           /               \
      Parameters          Body   [ MethodCallExpression: c.TestMethod() ]
          |              /    \
          |             /      \
    1: MyClass c    Object [c]  \
                                /\
                               /  \
                              /    \
             Method [TestMethod]  Arguments [Empty]

То, что вы хотите - это имя метода, внутри выражения вызова метода, внутри тела лямбда-выражения. Таким образом, код, чтобы получить это:

static string GetInnerMethodName<T>(Expression<Action<T>> expr)
{
    MethodCallExpression mce = expr.Body as MethodCallExpression;
    return (mce != null) ? mce.Method.Name : null;
}

Конечно, это будет работать только в том случае, если переданный Expression<Action<T>> является подлинным выражением вызова метода; потребитель этого метода технически может передать любое выражение, и в этом случае это просто вернет null. Вы можете настроить это, чтобы вместо этого выдавать исключение, возвращать значение по умолчанию или выполнять любые другие действия, которые вы считаете подходящими.

Вам не нужно делать ничего особенного, чтобы использовать это - это то же использование, что и ваш оригинальный метод:

string methodName = GetInnerMethodName<MyClass>(c => c.TestMethod());
0 голосов
/ 06 апреля 2010

То, что b__8, - это имя метода, сгенерированного компилятором C # для вашей лямбды. Это можно увидеть, например, используя Reflector .

Если вам нужно сказать «Создать», вам придется создать метод с именем Create. И, конечно, он должен вписываться в Action, поэтому он должен будет возвращать void.


В простых случаях вы можете создать метод, который принимает строку, указывающую вызываемый метод, и возвращать действие, вызывающее этот метод, для объекта указанного типа. Например, этот урок .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...