我一直在为一个项目构建一个小型访问规则模块,其中每个特定规则都由一个通用的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
个对象?
答案 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
的类型参数用作输入参数。
这是一个重要的教训......