3

質問をより適切に表現する方法がわかりませんが、ジェネリック インターフェイスのディクショナリを複数回作成しようとすると、次の問題に遭遇しました。多くの場合、これは、さまざまなタイプを処理するレジストリ タイプ コレクションを作成しようとしたときに発生します。

namespace GenericCollectionTest
{
    [TestFixture]
    public class GenericCollectionTest
    {
        interface IValidator<T>
        {
            bool Validate(T item);
        }

        class TestObject
        {
            public int TestValue { get; set; }
        }

        private Dictionary<Type, IValidator<object>> Validators = new Dictionary<Type, IValidator<object>>();

        class BobsValidator : IValidator<TestObject>
        {
            public bool Validate(TestObject item)
            {
                if (item.TestValue != 1)
                {
                    return false;
                }
            }
        }

        [Test]
        public void Test_That_Validator_Is_Working()
        {
            var Test = new TestObject {TestValue = 1};
            Validators.Add(typeof(BobsValidator), new BobsValidator());

            Assert.That(Validators[typeof(TestObject)].Validate(Test));
        }
    }
}

ただし、BobsValidator をパラメータ タイプ IValidator に割り当てることができないため、コンパイルは失敗します。基本的に、バリデータの外ではタイプ セーフは必要ありませんが、内部に入ると、インターフェースの消費者が使用したいタイプにキャストする必要はありません。

Java では、次のことができます。

Dictionary<Type, IValidator<?>>

私はこのようなことができることを知っています(ala IEnumerable):

interface IValidator
{
    bool Validate(object item);
}

interface IValidator<T> : IValidator
{
    bool Validate(T item);
}

abstract class ValidatorBase<T> : IValidator<T>
{
    protected bool Validate(object item)
    {
        return Validate((T)item);
    }

    protected abstract bool Validate(T item);
}

次に、代わりにIValidatorを辞書に取り、ValidatorBaseを拡張しますが、私が見ていないより良い方法があるに違いないようです。それとも、これは全体的なデザインが悪いだけですか? 次のような構造が必要なようです。

WhatTheHecktionary<T, IValidator<T>>

ありがとう!

4

1 に答える 1

0

BobsValidator を IValidator に割り当てるには、インターフェイスの汎用パラメーターを共変にする必要があります。これにより、IValidator は IValidator のようなより具体的な型を指すことができます。

interface IValidator<out T>
{
   bool Validate(T item);
}

ただし、インターフェイスがタイプセーフでなくなったためコンパイルできないことがわかり、コンパイラは許可しません。では、なぜタイプ セーフでなくなったのでしょうか。これを想像してください:

using NUnit.Framework;
namespace GenericCollectionTest
{
    [TestFixture]
    public class GenericCollectionTest
    {
        //.NET Compiling Error:
        //"Invalid variance: The type parameter 'T' must be contravariantly valid ..."
        interface IValidator<out T>
        {
            //Error: "Parameter must be type-safe. Invalid variance..."
            bool Validate(T item);
        }

        class MyObject
        {
            public int TestValue { get; set; }
        }

        class YourObject
        {
            public int CheckValue { get; set; }
        }

        class MyValidator : IValidator<MyObject>
        {
            public bool Validate(MyObject item)
            {
                return (item).TestValue == 1;
            }
        }

        class YoursValdator : IValidator<YourObject>
        {
            public bool Validate(YourObject item)
            {
                return (item).CheckValue == 1;
            }
        }

        [Test]
        public void Test_That_Validator_Is_Working()
        {
            //.NET compiler tries to prevent the following scenario:

            IValidator<object> someObjectValidator = new MyValidator();
            someObjectValidator.Validate(new YourObject()); // Can't use MyValidator to validate Yourobject

            someObjectValidator = new YoursValdator();
            someObjectValidator.Validate(new MyObject()); // Can't use YoursValidator to validate MyObject

        }
    }
}

これを回避するには、非ジェネリックを基底クラスとして使用して、バリデーターを辞書に保存できるようにすることをお勧めします。あなたのケースで以下が機能するかどうかを確認してください。

using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace GenericCollectionTest
{
    [TestFixture]
    public class GenericCollectionTest
    {

        interface IValiadtor
        {
            bool Validate(object item);
        }

        abstract class ValidatorBase<T> : IValidator<T>
        {
            public bool Validate(object item)
            {
                return Validate((T)item);
            }

            public abstract bool Validate(T item);
        }

        interface IValidator<T> : IValiadtor
        {
            //Error: "Parameter must be type-safe. Invalid variance..."
            bool Validate(T item);
        }

        class MyObject
        {
            public int TestValue { get; set; }
        }

        class YourObject
        {
            public int CheckValue { get; set; }
        }

        class MyValidator : ValidatorBase<MyObject>
        {
            public override bool Validate(MyObject item)
            {
                return (item).TestValue == 1;
            }
        }

        class YoursValdator : ValidatorBase<YourObject>
        {
            public override bool Validate(YourObject item)
            {
                return (item).CheckValue == 1;
            }
        }

        [Test]
        public void Test_That_Validator_Is_Working()
        {
            Dictionary<Type, IValiadtor> Validators = new Dictionary<Type, IValiadtor>();
            Validators.Add(typeof(MyObject), new MyValidator() );
            Validators.Add(typeof(YourObject), new YoursValdator());

            var someObject = new MyObject();
            someObject.TestValue = 1;
            Assert.That(Validators[someObject.GetType()].Validate(someObject));


        }
    }
}
于 2014-03-24T05:47:38.203 に答える