1

私は、すべての特定のルールが汎用Rule<TEntity>オブジェクトによって反映されるプロジェクト用の小さなアクセスルールモジュールを構築してきました。ルールは、特定のロジックを実行するためにデリゲートを取ります。

RulesContext次のような特定のエンティティ「foo」へのアクセスをチェックするメソッドを提供するクラスがあります。

rulesContext.CanIRead<Foo>(myFoo);

私の意図は、セットアッププロセス中に作成されたすべてのルールを1つのコレクションに保存することでした。しかし、私が試みたすべてのアプローチは行き止まりにつながりました。

私は次のようなことを考えました:

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には当てはまらないパラメーターとして、任意のオブジェクトで辞書のメソッドを使用できることを示しています。-リスコフの原則)

しかし、私はこれを解決する方法を考えることができません。Rule異なるタイプのオブジェクトを1つのコレクションに保存するにはどうすればよいですか?

4

4 に答える 4

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キーワードintは、Tがoutのみ(共変)であることを意味します。ここでそれについて読むことができます。あなたの場合はoutが問題になると思いますが、ルールにはTEntityが引数として渡されたメソッドがあるのではないかと思います。

于 2012-10-31T14:13:02.270 に答える
1

それは本質的にタイプセーフではありません。
あなたが書くならあなたは何をしたいですか

_rules[typeof(Foor)].CanRead(new Bar());

ディクショナリに格納するには、非ジェネリックの基本クラスまたはインターフェイスを作成する必要があります。

于 2012-10-31T14:07:03.620 に答える
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;
      }
   }
}
于 2012-11-09T10:36:10.880 に答える
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で、実際には全体がより専門的になります。

その理由は、のtype引数がRule入力パラメーターとして使用されるためです。

それは重要な教訓です...

于 2012-10-31T14:51:32.547 に答える