Is there any way to combine these almost identical classes into one?

那年仲夏 提交于 2019-12-10 14:34:10

问题


Followup to this question: Why is Nullable<T> considered a struct and not a class?

I have two classes that essentially maintain a tuple of some user-supplied value with an internal object.

When the type of the user-supplied value is a primitive, I have to wrap it in a Nullable<T> so that it can take on a null value in the Tuple.

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); }
    }
    //...

I'd prefer it if I could do this with a single class that can take either primitives or classes as a type parameter, but I don't see any way around it. Not without coming up with some sort of custom Nullable class that can box any type (not just types where T:struct) so as to ensure that Value can always be assigned null;

It seems like I should at least be able to define the latter class as

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

But even that fails since Nullable<T> does not meet the : class constraint (as per the linked question).


回答1:


If you simply designed your class like this:

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); }
    }
}

Then you can use it with a struct by specifying the type parameter as a Nullable<T>, for example:

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

The only problem here is that it's possible that a user could use this class with a non-nullable struct—e.g. Bundled<int>. If that's really a concern in your application, you could declare more specific sub-types like this:

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

You also can make the constructor for Bundled<T> internal so it can't be called from outside your assembly. This will ensure that the user doesn't create a custom sub-type to bypass your BundledClass / BundledStruct wrappers.




回答2:


Best I could come up with just now. It's still two classes, but it saves me from hundreds of lines of code duplication:

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); }
    }
}


来源:https://stackoverflow.com/questions/20597586/is-there-any-way-to-combine-these-almost-identical-classes-into-one

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!