如何创建Enumerable <func <>&gt;超出方法实例</func <>

时间:2013-08-27 09:46:32

标签: c#-4.0 reflection

我正在创建一个看起来有点像单元测试框架的规则集引擎。

[RuleSet(ContextA)]
public class RuleSet1
{
    [Rule(TargetingA)]
    public Conclusion Rule1(SubjectA subject)
    { Create conclusion }

    [Rule(TargetingA)]
    public Conclusion Rule2(SubjectA subject)
    { Create conclusion }

    [Rule(TargetingB)]
    public Conclusion Rule3(SubjectB subject)
    { Create conclusion }
}

    [RuleSet(ContextB)]
public class RuleSet2
{
    [Rule(TargetingB)]
    public Conclusion Rule1(SubjectB subject)
    { Create conclusion }

    [Rule(TargetingA)]
    public Conclusion Rule2(SubjectA subject)
    { Create conclusion }

    [Rule(TargetingB)]
    public Conclusion Rule3(SubjectB subject)
    { Create conclusion }
}

public class Conclusion()
{
    // Errorcode, Description and such
}
// contexts and targeting info are enums.

目标是创建一个可扩展的规则集,该规则集不会改变消费者POV中的API,同时在代码文件中具有良好的关注点分离。再次:像单元测试框架。

我正在尝试创建一个公开以下API的库

public static class RuleEngine
{
    public static IEnumerable<IRuleSet> RuleSets(contextFlags contexts)
    {
        {
            return from type in Assembly.GetExecutingAssembly().GetTypes()
                   let attribute =
                       type.GetCustomAttributes(typeof (RuleSetAttribute), true)
                           .OfType<RuleSetAttribute>()
                           .FirstOrDefault()
                   where attribute != null
                   select  ?? I don't know how to convert the individual methods to Func's.
        }
    }
}

internal interface IRuleset
{
    IEnumerable<Func<SubjectA, Conclusion>> SubjectARules { get; }
    IEnumerable<Func<SubjectB, Conclusion>> SubjectBRules { get; }
}

...允许消费者像这样简单地使用(在本例中使用foreach代替LINQ以便于阅读)

foreach (var ruleset in RuleEgine.RuleSets(context))
{
    foreach (var rule in ruleset.SubjectARules)
    {
        var conclusion = rule(myContextA);
        //handle the conclusion
    }
}

此外,如果你能告诉我如何摆脱“TargetingA”和“TargetingB”作为RuleAttribute参数,而是使用反射直接检查装饰方法的参数类型,将会非常有帮助。一直保持相同的简单外部API。

2 个答案:

答案 0 :(得分:1)

您可以使用Delegate.CreateDelegateGetParameters方法执行您想要的操作。

public class RuleSet : IRuleSet
{
    public IEnumerable<Func<SubjectA, Conclusion>> SubjectARules { get; set; }
    public IEnumerable<Func<SubjectB, Conclusion>> SubjectBRules { get; set; }
}

public static class RuleEngine
{
    public static IEnumerable<IRuleSet> RuleSets() // removed contexts parameter for brevity
    {
        var result = from t in Assembly.GetExecutingAssembly().GetTypes()
                     where t.GetCustomAttributes(typeof(RuleSetAttribute), true).Any()
                     let m = t.GetMethods().Where(m => m.GetCustomAttributes(typeof(RuleAttribute)).Any()).ToArray()
                     select new RuleSet 
                     {
                        SubjectARules = CreateFuncs<SubjectA>(m).ToList(), 
                        SubjectBRules = CreateFuncs<SubjectB>(m).ToList()
                     };
        return result;
    }
}

// no error checking for brevity
// TODO: use better variable names
public static IEnumerable<Func<T, Conclusion>> CreateFuncs<T>(MethodInfo[] m)
{
    return from x in m 
           where x.GetParameters()[0].ParameterType == typeof(T)
           select (Func<T, Conclusion>)Delegate.CreateDelegate(typeof(Func<T, Conclusion>), null, x);
}

然后你可以像这样使用它:

var sa = new SubjectA();
foreach (var ruleset in RuleEngine.RuleSets())
{
    foreach (var rule in ruleset.SubjectARules)
    {
        var conclusion = rule(sa);
        // do something with conclusion
    }
}

答案 1 :(得分:0)

在您的LINQ查询中,您直接前往RuleSetAttribute,因此丢失了其他信息。如果您在多行代码中断查询,则可以使用GetMethods()从类型中获取方法,然后您可以调用GetCustomAttribute<RuleAttribute>()