Wil*_*sem 4 c# generics constructor wildcard
假设你有一个泛型类Foo:
public class Foo<T> {
public T Data {
get;
protected set;
}
}
Run Code Online (Sandbox Code Playgroud)
是否可以定义仅在T继承(或是)特定类型时才适用的构造函数.
比如说T是int:
public Foo () {
this.Data = 42;
}
Run Code Online (Sandbox Code Playgroud)
应在编译时检查类型约束.这可能对优化很有用.比如说你有一个IEnumerable<T>并且你想做一个"缓存"(因为LINQ查询可能非常昂贵).现在如果IEnumerable<T>已经是IList<T>,那么不复制数据是有用的.另一方面,如果它实际上是LINQ查询,则另一个构造函数可以将数据存储在数组中.
作为一种解决方法,当然可以继承Foo(例如IntFoo)并在那里定义构造函数:
public class IntFoo : Foo<int> {
public IntFoo () {
this.Data = 42;
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这种方法的一个问题是private数据无法访问(或者必须进行数据处理protected).是否存在其他一些缺点,或者是否应该以这种方式对特定于类型的构造函数进行建模?
Jef*_*hao 11
你可以在这里申请一个技巧.它适用于许多场景.
internal static class FooHelper
{
private static class DefaultData<T>
{
public static T Value = default(T);
}
static FooHelper()
{
DefaultData<int>.Value = 42;
DefaultData<string>.Value = "Hello World";
}
// From @JeffreyZhao:
//
// Use a static method to trigger the static constructor automatically,
// or we need to use RuntimeHelpers.RunClassConstructor to make sure
// DefaultData is corrected initialized.
//
// The usage of RuntimeHelpers.RunClassConstructor is kept but commented.
// Using GetDefault<T>() is a better approach since static Foo() would be
// called multiple times for different generic arguments (although there's
// no side affect in this case).
//
// Thanks to @mikez for the suggestion.
public static T GetDefault<T>()
{
return DefaultData<T>.Value;
}
}
public class Foo<T>
{
/* See the comments above.
static Foo()
{
RuntimeHelpers.RunClassConstructor(typeof(FooHelper).TypeHandle);
}
*/
public T Data { get; protected set }
public Foo()
{
Data = FooHelper.GetDefault<T>();
}
}
Run Code Online (Sandbox Code Playgroud)
您可以为有限类型指定默认值,并将它们的结果保留为默认值.
这个技巧在实践中有几个变化.在我的项目中,我们使用泛型ITypeConverter<T>而不是内置TypeConverter来避免不必要的装箱:
public interface ITypeConverter<T>
{
bool CanConvertTo<TTarget>();
TTarget ConvertTo(T value);
}
Run Code Online (Sandbox Code Playgroud)
同样的技巧可以应用为:
public class LongConverter : ITypeConverter<long>
{
private static class Op<TTarget>
{
public static Func<long, TTarget> ConvertTo;
}
static LongConverter()
{
Op<string>.ConvertTo = v => v.ToString();
Op<DateTime>.ConvertTo = v => new DateTime(v);
Op<int>.ConvertTo = v => (int)v;
}
public TTarget ConvertTo<TTarget>(T value)
{
return Op<TTarget>.ConvertTo(value);
}
}
Run Code Online (Sandbox Code Playgroud)
优雅,快速,干净.
| 归档时间: |
|
| 查看次数: |
421 次 |
| 最近记录: |