创建通用字典协方差的集合

本文关键字:方差 集合 字典 创建 | 更新日期: 2023-09-27 17:51:19

我不知道如何更好地表达这个问题,但是我在多次尝试创建通用接口的Dictionary时遇到了以下问题。当尝试创建处理不同类型的注册表类型集合时,通常会出现这种情况:

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>>

谢谢!

创建通用字典协方差的集合

为了将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));

        }
    }
}