我不确定如何更好地说出这个问题,但是我尝试不止一次地创建泛型接口字典时遇到了以下问题。在尝试创建处理不同类型的注册表类型集合时,通常会出现这种情况:
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>>
谢谢!
答案 0 :(得分: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));
}
}
}