将一般 class 委派给特定 class 人员

Delegating general class to specific classes

我有以下接口:

public interface IModel
{
    ModelTypes ModelType { get; } // ModelTypes is an enum
}

public interface IModelConverter<T>
{
    byte[] ToBytes(T model);
}

此外,我有 3 个 IModel 的实现:ModelAModelBModelC,以及以下 classes:

public class ModelAConverter : IModelConverter<ModelA>

public class ModelBConverter : IModelConverter<ModelB>

public class ModelCConverter : IModelConverter<ModelC>

我想使用 IModel 并用 ToBytes 方法转换它。显然,我不希望调用者知道转换器的每一个实现,所以我创建了 DelegatingConverter class:

public class DelegatingConverter : IModelConverter<IModel>
{
    private readonly Dictionary<ModelTypes, IModelConverter<IModel>> _modelConverters;

    public DelegatingConverter()
    {
        _modelConverters = new Dictionary<ModelTypes, IModelConverter<IModel>>
        {
            {ModelTypes.TypeA, new ModelAConverter()}, // Argument type ModelAConverter is not assignable to parameter type IModelConverter<IModel>
            {ModelTypes.TypeB, new ModelBConverter()}, // Argument type ModelBConverter is not assignable to parameter type IModelConverter<IModel>
            {ModelTypes.TypeC, new ModelCConverter()}  // Argument type ModelCConverter is not assignable to parameter type IModelConverter<IModel>
        };
    }

    public byte[] ToBytes(IModel model)
    {
        // Here is the delegation..
        return _modelConverters[model.ModelType].ToBytes(model);
    }
}

一切顺利,直到我向委托字典添加一些转换器,_modelConverters

错误是:

Argument type ModelXConverter is not assignable to parameter type IModelConverter<IModel>

而且我知道这里的解决方案应该是在 IModelConverter 中对 T 使用协方差,所以 decleration 应该是:

public interface IModelConverter<out T>

我添加的时候出现如下错误:

Parameter must be input-safe. Invalid variance: The type parameter 'T' must be contravariantly valid on IModelConverter.ToBytes(T). 'T' is covariant.

有什么办法可以让它变得更好吗?我知道我可以让每个 ModelConverter 实现都实现 IModelConverter<IModel>,但是我需要在每个实现的开头进行明显的转换。

And I know the solution here should be using covariance on T in IModelConverter

不,不是真的。只有当任何 specific IModelConverter 可以被视为更 general IModelConverter 时才会出现这种情况 - 而事实并非如此案子。如果转换器只知道如何将 ModelB 转换为字节,您希望它用 ModelC 做什么?

最简单的方法可能是这样写:

// This class is general...
public sealed class DelegatingConverter<T> : IModelConverter<IModel>
    where T : IModel
{
    private readonly IModelConverter<T> originalConverter;

    public DelegatingConverter(IModelConverter<T> originalConverter)
    {
        this.originalConverter = originalConverter;
    }

    public byte[] ToBytes(IModel model)
    {
        return originalConverter.ToBytes((T) model);
    }
}

然后:

public sealed class KnownModelConverter : IModelConverter<IModel>
{
    private static readonly Dictionary<ModelTypes, IModelConverter<IModel>>
        = new Dictionary<ModelTypes, IModelConverter<IModel>>
    {
        { ModelTypes.TypeA, new DelegatingConverter<ModelA>(new ModelAConverter()) },
        { ModelTypes.TypeB, new DelegatingConverter<ModelB>(new ModelBConverter()) },
        { ModelTypes.TypeC, new DelegatingConverter<ModelC>(new ModelCConverter()) },
    };

    public byte[] ToBytes(IModel model)
    {
        // Here is the delegation..
        return _modelConverters[model.ModelType].ToBytes(model);
    }
}

基本上关键是 DelegatingConverter<T> 中的转换 - 你需要确保你只使用正确类型的传递实例到它的 ToBytes 方法 - 但假设 model.ModelType 是正确的,那应该没问题。 (如果不是,异常可能是正确的行为。)

您可以显式地强制转换它以避免像

这样的错误
public DelegatingConverter()
{
    _modelConverters = new Dictionary<ModelTypes, IModelConverter<IModel>>
    {
        {ModelTypes.TypeA, (IModelConverter<IModel>)new ModelAConverter()},
        {ModelTypes.TypeB, (IModelConverter<IModel>)new ModelBConverter()},
        {ModelTypes.TypeC, (IModelConverter<IModel>)new ModelCConverter()}
    };
}