继承泛型类型和约束时键入推断

时间:2013-07-30 17:44:25

标签: c# .net generics interface

我正在构建一个接口,允许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>>();

2 个答案:

答案 0 :(得分:2)

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

另一方面,如果你翻转并只提供TransactionStatus类型,暗中希望反射会以某种方式解决你的问题,那么两个不同的类可能都会实现IStringToTypeConverter<TransactionStatus>。你还有一个问题,你不知道哪一个对你的问题是正确的。同样,没有领域知识,这个问题对编译器来说是难以处理的。

答案 1 :(得分:1)

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中),那么您可以让它们全部继承自实现IStringToTypeConverterUntyped中声明的成员的基类ConverterBase并声明在IStringToTypeConverter&lt; T &gt;中声明的成员的抽象成员。