我正在构建一个接口,允许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>>();
?
答案 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;中声明的成员的抽象成员。