C# 有没有办法把这些几乎相同的类合并成一个类?

C# 有没有办法把这些几乎相同的类合并成一个类?,c#,generics,nullable,type-constraints,C#,Generics,Nullable,Type Constraints,本问题的后续行动: 我有两个类,它们基本上是用一个内部对象维护用户提供的值的元组 当用户提供的值的类型是原语时,我必须将其包装在Nullable中,以便它可以在元组中接受null值 public class BundledClass<T> where T : class { private Tuple<T, object> _bundle; public T Value { get { return _bundle == null

本问题的后续行动:

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

当用户提供的值的类型是原语时,我必须将其包装在
Nullable
中,以便它可以在元组中接受null值

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
不符合
:class
约束(根据链接的问题)。

如果您只是这样设计类:

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); }
    }
}
public class BundledClass<T> : Bundled<T> where T : class { }
public class BundledStruct<T> : Bundled<T?> where T : struct { }
绑定的公共抽象类
{
私有元组束;
公共价值
{ 
获取{return _bundle==null?默认值(T):_bundle.Item1;}
set{u bundle=新元组(值,internalObj);}
}
}
然后,您可以通过将类型参数指定为可为null的
来将其与结构一起使用,例如:

Bundled<string> foo; // handles classes
Bundled<float?> bar; // handles structs
Bundled foo;//处理类
捆扎钢筋;//句柄结构
这里唯一的问题是,用户可能会将此类与不可为空的结构一起使用,例如捆绑的
。如果应用程序中确实存在这一问题,则可以声明更具体的子类型,如下所示:

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); }
    }
}
public class BundledClass<T> : Bundled<T> where T : class { }
public class BundledStruct<T> : Bundled<T?> where T : struct { }
公共类BundledClass:Bundled其中T:class{}
公共类BundledStruct:Bundled,其中T:struct{}

您还可以将
绑定的
构造函数设置为内部构造函数,这样就不能从程序集外部调用它。这将确保用户不会创建自定义子类型来绕过您的
BundledClass
/
BundledStruct
包装器。

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

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); }
    }
}
绑定的公共抽象类
{
受保护元组束;
公共抽象T值{get;set;}
//…其他一切
}
公共类BundledClass:绑定,其中T:class
{
公共密封覆盖T值
{ 
获取{返回_bundle==null?null:_bundle.Item1;}
set{u bundle=新元组(值,internalObj);}
}
}
公共类BundledPrimitive:绑定,其中T:struct
{        
公共密封覆盖T?值
{ 
获取{返回_bundle==null?null:_bundle.Item1;}
set{u bundle=新元组(值,internalObj);}
}
}

无法使用
默认值(t)
。如果用户创建了一个
绑定的foo
,我绝对必须区分他们何时设置了
foo.Value=0
和何时未设置
foo.Value
(因此
\u bundle=Tuple(null,internalObj)
),我想这和下面的答案一样有效,只要没有人试图在没有类型约束的情况下实现捆绑的。@Alain如我所说,您可以将构造函数设置为内部构造函数,因此只能从程序集中调用它。这意味着从捆绑的
派生的任何外部类型都不会编译(至少在没有大量反射的情况下不会编译)。如果希望用户能够从公共构造函数继承,请确保
BundledClass
/
BundledStruct
提供公共构造函数。这可以与静态构造函数中的运行时检查相结合,以确保
T
是“好的”。这是因为没有编译时泛型约束。静态构造函数应该去
static Bundled(){if(typeof(T).IsValueType&&Nullable.getunderyingtype(typeof(T))==null)抛出新的invalidoOperationException(“T必须允许null”);}
您可以将这种方法与@Alain结合起来,使
Bundled
抽象,而无需向其添加任何虚拟方法。这不会阻止同一程序集中的代码从中派生,但它不会像在同一程序集中一样可用,并且对于内部ctor,它将禁止任何其他程序集使用。如果我是此类的用户,如何区分未设置的
和已在程序中的其他地方将
设置为null的我?@mikez如果从未设置过该值,则内部
\u bundle=null
。如果
Value
设置为null,则内部
\u bundle
将是一个
元组(null,internalObj)
。这并不重要,但用户可以使用返回
\u bundle
的public get-only属性看到这一点。是的,这将是内部状态的区别,但作为调用者,我无权访问
\u bundle
@Alain BTW,我在回答中选择了名称
BundledStruct
而不是
BundledPrimitive
,因为两者并不完全相同。
DateTime
是一个结构,但不是基元,
string
是C#中的基元(尽管
Type.IsPrimitive
将返回false),但它不是一个结构。