【问题标题】:Multiple generic types in same list and calling their methods同一列表中的多个泛型类型并调用它们的方法
【发布时间】:2012-08-25 20:25:35
【问题描述】:

我正在利用业余时间制作一个对象验证框架来学习一些东西,并可能将其用于一些学校项目。

我有我的通用 Rule 类,看起来像这样:

class Rule<T>
{
    string propertyName;
    Func<T, bool> ruleLambda;

    bool IsBroken(T value)
    {
        return ruleLambda(value);
    }
}

将被验证的对象看起来有点像这样:

class Example
{
    List<Rule<?>> MyRules; // can take all types of rules

    List<Rule<T>> Validate<T>(string propertyName, T value)
    {
        List<Rule<T>> brokenRules = new List<Rule<T>>();
        foreach (Rule rule in MyRules.Where(r => r.propertyName == propertyName))
        {
            if (rule.IsBroken(value))
                brokenRules.Add(rule);
        }
        return brokenRules;
    }
}

T value 参数是 Example 类属性之一的值,可以是任何类型。

只要设置了属性,就会调用Validate&lt;T&gt; 方法。

问题在于类的规则列表。特别是上面的List&lt;Rule&lt;?&gt;&gt; 行。我想将给定类的所有规则存储在同一个列表中。

唉,C# 没有像 Java 这样的泛型类型的通配符。

我应该怎么做?

使用对象而不是 T 的非泛型接口或基类可以工作,但我如何调用泛型 Rule 的 IsBroken 方法而不是非泛型方法?

【问题讨论】:

  • 定义一个接口 IRule,具有非通用接口,例如bool IsBroken (object),有Rule&lt;X&gt;: IRule,有List&lt;IRule&gt; .. 好吧,它不是很漂亮,但它确实有效,并且是我见过的最好的。

标签: c# generics


【解决方案1】:

我会将您的规则存储为object 内的Example 类并使用Enumerable.OfType&lt;T&gt; 查找给定类型的匹配规则:

class Example
{
    private List<object> rules;

    List<Rule<T>> Validate<T>(string propertyName, T value)
    {
        return this.rules.OfType<Rule<T>>()
            .Where(r => r.PropertyName == propertyName && r.IsBroken(value))
            .ToList();
    }
}

【讨论】:

    【解决方案2】:

    如果我需要这样的东西,我会使用接口或非泛型基类。例如,您可以创建一个接口:

    public interface IRule
    {
      //non-generic properties & methods
    }
    
    public class Rule<T> : IRule
    {
      //implementation
    }
    

    然后创建一个接口列表:

    private List<IRule> MyRules;
    

    如果你想让从接口到泛型的转换变得容易,你可以添加一个扩展方法:

    public static Rule<T> ToGeneric<T>(this IRule rule)
    {
      return rule as Rule<T>;
    }
    

    【讨论】:

      【解决方案3】:

      我尝试了一些方法,并找到了一些非常适合我需要的方法。我有Rule&lt;T&gt; 继承自基本抽象规则类,具有通用IsBroken 方法:

      abstract class Rule
      {
          string propertyName;
          Func<object, bool> objectRule;
      
          bool IsBroken<T>(T value)
          {
              Rule<T> rule = this as Rule<T>;
              if (rule == null)
                  return objectRule(value);
      
              return rule.IsBroken(value);
          }
      }
      

      如您所见,我尝试使用IsBroken 方法中的泛型类型参数将基类转换为其泛型对应对象。

      另外,在创建Rule&lt;T&gt; 实例时,我将Func&lt;object, bool&gt; 发送到其基类受保护的构造函数:

      public Rule(string propertyName, Func<T, bool> ruleLambda)
          : base(propertyName, ConvertToObjectFunc(ruleLambda))
      {
      }
      

      转换方法如下:

      static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func)
      {
          return new Func<object, bool>(o => func((T)o));
      }
      

      但是,如果它不能将 o 转换为类型 T,它就会崩溃。所以我写了这个……东西:

      static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func)
      {
          return new Func<object, bool>
          (
              o =>
              {
                  try
                  {
                      T obj = (T)o;
                      return func(obj);
                  }
                  catch { return true; } // rule is broken by default
              }
          );
      }
      

      这很丑陋,但它确实有效。希望这可以帮助其他人。

      【讨论】: