继承泛型类型和约束时的类型推断
本文关键字:类型 泛型类型 约束 继承 | 更新日期: 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>。
小时>