在一个集合中保存不同类型的通用对象

时间:2012-10-31 14:04:09

标签: c# generics

我一直在为一个项目构建一个小型访问规则模块,其中每个特定规则都由一个通用的Rule<TEntity>对象反映出来。该规则使委托执行某个逻辑。

有一个RulesContext类提供检查对某个实体“foo”的访问的方法,如下所示:

rulesContext.CanIRead<Foo>(myFoo);

我的目的是将设置过程中构建的所有规则存储到一个集合中。但是我尝试过的每一种方法都会导致死路一条。

我想到了类似的东西:

IDictionary<Type, Rule<object>> _rules = new Dictionary<Type, Rule<object>>();

var fooRule = new Rule<Foo>(foo => foo.FullfillsACertainFooCriterion())
_rules.Add(typeof(Foo), fooRule);

CanIRead实现将确保正确使用字典:

public bool CanIRead<TEntity>(TEntity entity)
{
    var rule = _rules[typeof(entity)];
    return rule.CanIRead(entity);
}

但编译器不喜欢这样:Rule<Foo>无法分配给Rule<object>类型的参数。哪种有意义,因为它会违反合同(这表示我可以将字典的方法与任何对象一起用作参数,这对于仅接受Foo类型对象的fooRule不适用。 - Liskov原理)

然而,我想不出解决这个问题的方法。如何在一个集合中存储具有不同类型的Rule个对象?

4 个答案:

答案 0 :(得分:2)

你能做到这一点:

[TestFixture]
public class ContraVariance
{
    [Test]
    public void TestNameTest()
    {
        var rules = new List<IRule<object>>(); //object is used just for demo here, probably some interface of yours is better
        rules.Add(new Rule<A>());
        rules.Add(new Rule<B>());
    }
}
public class A { }
public class B { }

public class Rule<TEntity> : IRule<TEntity>
{

}

public interface IRule<out T>
{
}

如果不是,我认为你必须有一个非通用的IRule或RuleBase(类) 接口中的out关键字意味着T只是(Covariant)你可以阅读它here。 我想在你的情况下这将是一个问题,我怀疑规则有TEntity作为参数传递的方法。

答案 1 :(得分:1)

这本质上是非类型安全的 如果你写

,你想要发生什么?
_rules[typeof(Foor)].CanRead(new Bar());

您需要创建一个非通用的基类或接口来存储在字典中。

答案 2 :(得分:1)

不是使用IDictionary<Type, object>可以保存任何内容(例如DateTime)作为字典中的值,而是可以严格地使值成为规则对象

下面

namespace RuleConsole
{
   class Program
   {
      static void Main(string[] args)
      {
         var context = new RulesContext();

         var objA = new A();
         var objB = new B();

         context.AddRule<A>(new Rule<A>(objA));
         context.AddRule<B>(new Rule<B>(objB));

         Console.WriteLine(context.CanIRead<A>(objA));
         Console.WriteLine(context.CanIRead<B>(objB));

         Console.ReadKey();
      }
   }

   public interface IRule { }
   public interface IRule<T> : IRule { }

   public class Rule<T> : IRule<T> 
   {
      T _entity;
      public Rule(T entity)
      {
         _entity = entity;
      }
   }

   public class A { }
   public class B { }

   public class RulesContext
   {
      Dictionary<Type, IRule> _ruleDict= new Dictionary<Type, IRule>();

      public void AddRule<TEntity>(Rule<TEntity> rule)
      {
         _ruleDict.Add(typeof(TEntity), rule);
      }

      public bool CanIRead<TEntity>(TEntity entity)
      {
         var rule = (IRule<TEntity>)_ruleDict[typeof(TEntity)];

         //CanIRead implementation here

         return rule != null;
      }
   }
}

答案 3 :(得分:0)

嗯,这几乎令人尴尬 - 但我认为你刚刚帮我解开了大脑: - )

如果问题是IDictionary<Type, Rule<object>>过于具体,IDictionary<Type, object>可以解决问题:

var fooRule = new Rule<Foo>(foo => foo.FullfillsACertainFooCriterion())
_rules.Add(typeof(Foo), fooRule);

(与问题相同,但这次编译)

public bool CanIRead<TEntity>(TEntity entity)
{
    var rule = (Rule<TEntity>)_rules[typeof(entity)];
    return rule.CanIRead(entity);
}

我大脑中的阻滞者是我认为Rule<...>中的类型参数越通用,字典中应该允许的对象越多,但在这种情况下它是相反的:更多这个论点是通用的,合同越具体。

采取:

IDictionary<Rule<Foo>>

通过将Rule替换为其基类object,字典变得更通用。但是,通过将Foo替换为object,整个事情实际上变得更加专业化了!

这样做的全部原因是Rule的类型参数用作输入参数。

这是一个重要的教训......