如何从此代码重构我们的type参数?

时间:2010-06-22 20:17:28

标签: c# generics refactoring delegates inferred-type

我想编写一个扩展方法来测试是否在方法调用中应用了一个属性,并且我想将该方法指定为lambda表达式。目前,我有以下(工作)方法,但我真的不喜欢这段代码的样子:

// 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需要改变。我不想为我测试的方法的每个输入参数数量设置一个不同的实现...

更新
正如Jay在评论中指出的那样,我显然不需要在TDelegate的调用中指定HasAttribute<>类型参数。代码现在看起来像这样:

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

它好多了,但我仍然认为第一行非常混乱。可能会更好吗?

2 个答案:

答案 0 :(得分:1)

你可以做的一件事是用

替换长丑陋的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();

更新:以显示更新问题的示例。我认为它不会变得更加简化。

答案 1 :(得分:1)

  1. 您可以使用LambdaExpression代替通用格式Expression<>吗? (如果是这样,那么TDelegate param可以消失)
  2. 如果没有,这样可以吗:

    // Signature of my extension method:
    public static bool HasAttribute<TDelagate>(this Expression<TDelagate> method, 
                                               Type attributeType)
    
  3. 问题是,你有一种你想要明确指定的类型,另一种你不想要这种类型(这在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);
        }
    }