继承泛型类型和约束时的类型推断

本文关键字:类型 泛型类型 约束 继承 | 更新日期: 2023-09-27 18:25:29

我正在构建一个接口,以允许NHibernate从自定义类型转换为字符串,反之亦然。

public interface IStringToTypeConverter<T>
{
    T FromString(string value);
    string ToString(T value);
}

其中类型T可以是任何东西。对于这个例子,我将使用枚举,我知道NHibernate有转换枚举的功能,但这是问题的最简单的例子。

public enum TransactionStatus { Failed, Pending, Succeeded }
public class TransactionStatusConverter : IStringToTypeConverter<TransactionStatus>
{
    public TransactionStatus FromString(string value)
    {
        return (TransactionStatus)Enum.Parse(typeof (TransactionStatus), value, true);
    }
    public string ToString(TransactionStatus value)
    {
        return value.ToString();
    }
}

到目前为止还不错,但当我尝试将TransactionStatusConverter指定为具有泛型类型和约束IStringToTypeConverter<T>的类的泛型类型时,问题就出现了。

public sealed class CustomStringType<TConverter, TType> : IUserType where TConverter : IStringToTypeConverter<TType>, new()
{
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var value = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
        var converter = new TConverter();
        return converter.FromString(value);
    }
    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var converter = new TConverter();
        ((IDataParameter)cmd.Parameters[index]).Value
            = converter.ToString((TType)value);
    }
}

此类在NHibernate:中的用法

Property(x => x.Status, map =>
{
    map.Column("TransactionStatusID");
    map.Type<CustomStringType<TransactionStatusConverter, TransactionStatus>>();
}); 

其中,Type<>()函数的类型约束为IUserType

我的问题是,我觉得在map.Type<CustomStringType<TransactionStatusConverter, TransactionStatus>>();中,TransactionStatus是多余的,因为它应该通过转换器的类型来推断。

我该如何编写CustomStringType,以便我的映射代码读取为map.Type<CustomStringType<TransactionStatusConverter>>();

继承泛型类型和约束时的类型推断

如果您可以省略类型参数,那么没有什么可以阻止您拥有一个同时实现IStringToTypeConverter<Foo>IStringToTypeConverter<Bar>的类。如果该类同时执行了这两个操作,那么它可以安全地推断出应该使用哪种类型作为CustomStringType的第二个类型参数?记住:你只能做出编译器可以做出的逻辑假设,而不能欺骗和使用你的领域知识来解决问题!

另一方面,如果您翻转它,只提供TransactionStatus类型,私下里希望反射能以某种方式解决您的问题,那么两个不同的类可能都实现了IStringToTypeConverter<TransactionStatus>。你仍然有一个问题,你不知道哪一个问题对你的问题是正确的。同样,在没有领域知识的情况下,这个问题对编译器来说是棘手的。

C#不支持泛型类型参数的部分推理。

一种方法是使用using alias指令:

using CustomStringTypeTransactionStatusConverter = CustomStringType<TransactionStatusConverter, TransactionStatus>;

然后你可以这样做:

map.Type<CustomStringTypeTransactionStatusConverter>();

注意,上面的方法比定义子类要好,因为基类和子类是不同的类型(如果您认为它们在语义上是相同的类型,并希望在方法参数等中使用它们,这是不好的):

// This is bad because it defines a new type
public class SubClass : CustomStringType<TransactionStatusConverter, TransactionStatus>
{
}
<小时>

然而,在您的特定情况下,您可以进行一些简单的重构(请注意,在代码中,您只使用TType来强制转换对象,而这种强制转换可以在TransactionStatusConverter本身中完成)

首先介绍一个额外的接口:

public interface IStringToTypeConverter<T> : IStringToTypeConverterUntyped
{
    T FromString(string value);
    string ToString(T value);
}
public interface IStringToTypeConverterUntyped
{
    object FromStringUntyped(string value);
    string ToString(object value);
}

实施成员:

public class TransactionStatusConverter : IStringToTypeConverter<TransactionStatus>
{
    public TransactionStatus FromString(string value)
    {
        return (TransactionStatus)Enum.Parse(typeof(TransactionStatus), value, true);
    }
    public object FromStringUntyped(string value)
    {
        return FromString(value);
    }
    public string ToString(TransactionStatus value)
    {
        return value.ToString();
    }
    public string ToString(object value)
    {
        return ToString((TransactionStatus)value);
    }
}

并修改CustomStringType:

public sealed class CustomStringType<TConverter>
    where TConverter : IStringToTypeConverterUntyped, new()
{
}

现在你可以这样使用它:

map.Type<CustomStringType<TransactionStatusConverter>>();

附加说明:如果您有许多转换器,并且不想在每个转换器中实现IStringToTypeConverterUntyped(例如在TransactionStatusConverter中),那么您可以让它们全部从基类ConverterBase继承,该基类实现在IStringToType ConverterUnyped中声明的成员,还为在IStringToTypeConverter<T>。