2013-08-27 58 views
2

我正在創建一個規則集引擎,看起來有點像單元測試框架。如何從方法實例中創建枚舉<Func<>>

[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。

回答

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

在你的LINQ查詢中,你直奔RuleSetAttribute,所以丟失了其他信息。如果您在多行代碼中打破查詢,則可以使用GetMethods()類型的方法獲取方法,然後您可以撥打GetCustomAttribute<RuleAttribute>()

+0

我仍然可以在我的select語句訪問 「類型」。這只是一個「let」語句,用於創建一個臨時變量,而不是用於轉換類型的「select」語句。順便說一句:這不應該是一個評論,而不是一個答案? – Tormod

+0

我相信我回答了主要問題,儘管我現在看到主要問題沒有被實際詢問。我認爲主要問題是如何收集所有方法的規則屬性。不是嗎? – Dialecticus

相關問題