Как я могу изменить наш параметр типа из этого кода? - PullRequest
3 голосов
/ 23 июня 2010

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

// Signature of my extension method:
public static bool HasAttribute<TAttribute, TDelegate>(this Expression<TDelegate> method)

// Usage (current code)
Expression<Func<AccountController, LogInModel, string, ActionResult>> mut = (c, m, s) => c.LogIn(m, s);
mut.HasAttribute<ExportModelStateAttribute, Func<AccountController, LogInModel, string, ActionResult>>().ShouldBeTrue();

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

// Usage (if I had my way...)
var mut = (c, m, s) => c.LogIn(m, s);
mut.HasAttribute<ExportModelStateAttribute>().ShouldBeTrue();

но я понимаю, что, возможно, слишком много просил.

Можно ли как-то реорганизовать аргументы типа из моего текущего кода?

Причина, по которой у меня есть аргумент типа TDelegate, заключается в том, что я хочу использовать его независимо от сигнатуры метода и типа возвращаемого значения, а также от количества входных аргументов и от того, является ли рассматриваемый метод void или функция, TDelegate должна меняться. Я не хочу иметь одну отдельную реализацию для каждого количества входных аргументов для метода, который я тестирую ...

Обновление:
Как отмечает Джей в комментарии, мне, очевидно, не нужно указывать аргумент типа TDelegate в вызове HasAttribute<>. Код теперь выглядит так:

Expression<Func<AccountController, LogInModel, string, ActionResult>> mut = (c, m, s) => c.LogIn(m, s);
mut.HasAttribute<ExportModelStateAttribute>().ShouldBeTrue();

Это намного лучше, но я все еще думаю, что первая строка довольно грязная. Может ли быть еще лучше?

Ответы [ 2 ]

1 голос
/ 23 июня 2010
  1. Можете ли вы использовать LambdaExpression вместо общей формы Expression<>?(если так, то параметр TDelegate может исчезнуть)
  2. Если нет, то все будет в порядке:

    // Signature of my extension method:
    public static bool HasAttribute<TDelagate>(this Expression<TDelagate> method, 
                                               Type attributeType)
    

Проблема в том, что у вас есть одинтип, который вы хотите указать явно, и другой, который вы не хотите (это невозможно в C #).

Независимо от того, можете ли вы выполнить любой из них, вам все равно нужно будет указать полный тип делегата дляобъявление выражения (по крайней мере в C # 3, не уверен насчет C # 4, если это законно или нет, я не знаю, есть ли способ обойти это или нет).

Если HasAttribute работает так, как я думаю, то, я думаю, вы можете сделать # 1:

public static bool HasAttribute<TAttribute>(this LambdaExpression method) {
    if (method.Body.NodeType == ExpressionType.Call) {
        MethodCallExpression call = (MethodCallExpression)method.Body;
        return call.Method.GetCustomAttributes(typeof(TAttribute), true).Any();
    }
    return false;
}

Редактировать:

Я думаю, вы можете упроститьпервая строка с такими функциями:

public static LambdaExpression GetMut<T>(Expression<Func<T>> f) { return f; }
public static LambdaExpression GetMut<T>(Expression<Action<T>> f) { return f; }

с использованием:

var l = new LogInModel(); // these variables can be inlined or declared 
var s = "";               // it makes no difference
var mut = Expressions.GetMut<AccountController>(c => c.LogIn(l,s));
mut.HasAttribute<ExportModelStateAttribute>().ShouldBeTrue();

полный код консольного приложения, используемого для игры с этим:

class Program {
    static void Main(string[] args) {
        var mut = Expressions.GetMut<AccountController>(c => c.LogIn(new LogInModel(), ""));
        mut.HasAttribute<ExportModelStateAttribute>().ShouldBeTrue();

        var failmut = Expressions.GetMut<AccountController>(c => c.LogInFails());
        failmut.HasAttribute<ExportModelStateAttribute>().ShouldBeTrue();

        Console.ReadKey();
    }
}

public class ExportModelStateAttribute : Attribute { }

public class ActionResult { }

public class LogInModel { }

public class AccountController {
    [ExportModelState]
    public ActionResult LogIn(LogInModel model, string s) {
        return new ActionResult();
    }

    public void LogInFails() {}
}

public static class Expressions {
    // only need this to find the method given the class T
    public static LambdaExpression GetMut<T>(Expression<Action<T>> func) { return func; }
    // Signature of my extension method:
    public static bool HasAttribute<TAttribute>(this LambdaExpression method) {
        if (method.Body.NodeType == ExpressionType.Call) {
            MethodCallExpression call = (MethodCallExpression)method.Body;
            return call.Method.GetCustomAttributes(typeof(TAttribute), true).Any();
        }
        return false;
    }

    public static void ShouldBeTrue(this bool obj) {
        Console.WriteLine(obj);
    }
}
1 голос
/ 23 июня 2010

Единственное, что вы можете сделать, это заменить длинную уродливую Func<AccountController, LogInModel, string, ActionResult> на

public delegate ActionResult myDelegate(AccountController accountController, LogInModel logInModel, string varString);

Expression<myDelegate> mut = (c, m, s) => c.LogIn(m, s);
mut.HasAttribute<ExportModelStateAttribute>().ShouldBeTrue();

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

...