有没有办法将这些几乎相同的类合并为一个?

时间:2013-12-15 17:37:23

标签: c# generics nullable type-constraints

对此问题的跟进:Why is Nullable<T> considered a struct and not a class?

我有两个类,基本上维护一个用户提供的值的元组和内部对象。

当用户提供的值的类型是基元时,我必须将其包装在Nullable<T>中,以便它可以在元组中采用空值。

public class BundledClass<T> where T : class
{
    private Tuple<T, object> _bundle;
    public T Value
    { 
        get { return _bundle == null ? null : _bundle.Item1; }
        set { _bundle = new Tuple<T, object>(value, internalObj); }
    }
    //...

public class BundledPrimitive<T> where T : struct
{
    private Tuple<T?, object> _bundle;
    public T? Value
    { 
        get { return _bundle == null ? null : _bundle.Item1; }
        set { _bundle = new Tuple<T?, object>(value, internalObj); }
    }
    //...

如果我能用一个可以将基元或类作为类型参数的类来做这个,我更喜欢它,但我没有看到任何解决方法。并非没有提出某种类型的自定义Nullable类,它可以包装任何类型(不仅仅是类型where T:struct),以确保始终可以为Value分配null;

似乎我至少应该能够将后一类定义为

public class BundledPrimitive<T> : BundledClass<T?> { }

但即便失败,因为Nullable<T>不符合: class约束(根据链接问题)。

2 个答案:

答案 0 :(得分:5)

如果您只是按照以下方式设计您的课程:

public abstract class Bundled<T>
{
    private Tuple<T, object> _bundle;
    public T Value
    { 
        get { return _bundle == null ? default(T) : _bundle.Item1; }
        set { _bundle = new Tuple<T, object>(value, internalObj); }
    }
}

然后,您可以通过将类型参数指定为Nullable<T>来将其与结构一起使用,例如:

Bundled<string> foo; // handles classes
Bundled<float?> bar; // handles structs

这里唯一的问题是,用户可以将此类与不可为空的结构一起使用 - 例如。 Bundled<int>。如果您的应用程序确实存在问题,您可以声明更具体的子类型,如下所示:

public class BundledClass<T> : Bundled<T> where T : class { }
public class BundledStruct<T> : Bundled<T?> where T : struct { }

您也可以为Bundled<T>内部构建一个构造函数,这样就无法从程序集外部调用它。这将确保用户不会创建自定义子类型以绕过您的BundledClass / BundledStruct包装。

答案 1 :(得分:2)

我现在能想到的最好的。它仍然是两个类,但它使我免于数百行代码重复:

public abstract class Bundled<T>
{
    protected Tuple<T, object> _bundle;
    public abstract T Value { get; set; }

    //... Everything else
}

public class BundledClass<T> : Bundled<T> where T : class
{
    public sealed override T Value
    { 
        get { return _bundle == null ? null : _bundle.Item1; }
        set { _bundle = new Tuple<T, object>(value, internalObj); }
    }
}

public class BundledPrimitive<T> : Bundled<T?> where T : struct
{        
    public sealed override T? Value
    { 
        get { return _bundle == null ? null : _bundle.Item1; }
        set { _bundle = new Tuple<T?, object>(value, internalObj); }
    }
}