3

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

4

2 回答 2

2

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

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

于 2013-07-30T18:10:57.873 回答
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< T > 中声明的成员。

于 2013-07-31T10:56:27.703 回答