【问题标题】:C# generic class using reference types and nullable value types使用引用类型和可为空值类型的 C# 泛型类
【发布时间】:2014-01-19 22:01:27
【问题描述】:

我有一个有趣的问题。我想创建一个可以处理引用类型和Nullable<T> 类型的泛型类。基本上我想要这样的东西:

public class ClassWithNull<T>
{
    public T varName = null;
}

现在这当然不能编译,因为并非所有类型都可以赋值为 null,即不可为 null 的值类型。但问题是Nullable&lt;T&gt; 是一个值类型,所以简单地添加where T : class 对我没有帮助。我的 generics-foo 不是太强,但我无法找到任何方法说 T 必须是引用类型或可为空的值类型。

我必须解决这个问题的想法是使ClassWithNull&lt;T&gt; 成为一个抽象类。然后我可以添加两个子类,一个处理引用类型,一个处理可空值类型。然后,基类中的静态工厂方法可以使用反射来确定应该构造哪个子类。比如:

public static ClassWithNull<T> CreateClassWithNull<T>()
{
    StackTrace st = new StackTrace();
    Type type = st.GetFrame(1).GetMethod().GetGenericArguments()[0];
    if (!type.IsValueType)
    {
        return new ClassWithReferenceType<T>();
    }
    else if (type == typeof(Nullable))
    {
        return new ClassWithNullableValueType<T>();
    }
    else
    {
        throw new Exception("Must provide nullable type.");
    }
}

这里的问题是泛型是静态解析的。如果ClassWithReferenceType&lt;U&gt; 期望U 是引用类型,那么在工厂方法中调用new ClassWithReferenceType&lt;T&gt;() 是一个编译错误,因为T 不需要是引用类型。编译器不知道运行时检查。

关于如何实现这样的事情有什么想法吗?

【问题讨论】:

  • 请注意,T x = null; 只会在 x 可以为空的情况下编译,但 T x = default(T)x == null总是编译(对于非可空类型)。
  • 在您的代码中,typeof(Nullable) 指的是另一种类型,而不是您想象的类型,即非泛型的静态类。相反,可以说type.IsGenericType &amp;&amp; type.GetGenericTypeDefinition() == typeof(Nullable&lt;&gt;)。在这种特定情况下,另一种选择是Nullable.GetUnderlyingType(type) != null。此外,您似乎可以使用typeof(T) 而不是您所谓的type

标签: c# generics reflection


【解决方案1】:

怎么样:

public class ClassWithNull<T>
{
    public T varName = default(T);
}

(实际上,您甚至不需要赋值 - 您可以将其保留为构造时的默认值。但您可能希望 default(T) 用于局部变量。)

这不会阻止您将其与不可为空的值类型错误地一起使用 - 但这是否足够?

如果这对您没有帮助,我建议编写 两个 静态方法,如下所示:

public static ClassWithNull<T> CreateClassWithNullForClass<T> where T : class
{
    return new ClassWithReferenceType<T>();
}

public static ClassWithNull<T> CreateClassWithNullForNullable<T> where T : struct
{
    return new ClassWithNullableValueType<T>();
}

ClassWithNullableValueType 中的字段将是 Nullable&lt;T&gt; - T 将是基础类型。

现在,如果您想要重载相同的方法,那就有点困难了,特别是如果您不想传递任何参数。 可能,但是really, really horrible

【讨论】:

  • 我认为他对 default(T) 不感兴趣,因为它在某些情况下不能为空。他的意图是只允许分配 null(“必须是引用类型或可为空的值类型”)。
  • @Wiktor: default(T) 对于任何具有空值的T 始终是空值。但是没有任何约束可以表达这一点。不过,请参阅我的其余答案以获取更多选项...
  • 您编辑的答案只包含我想到的解决方案。
  • 是的,第一个解决方案是不够的。不过第二个很精彩。我不知道为什么我从来没有想到有 两个 工厂方法。我会在三分钟内接受这个答案(为什么会阻止提问者在 10 分钟内接受答案?)
  • @WiktorZychla:太好了。如果可能的话,我想我可能仍然会坚持第一种方法:)
【解决方案2】:

您应该可以这样做:

public class ClassWithNull<T>
{
    private object varName_priv = null;

    public T varName {
        get { return (T)varName_priv; }
        set { varName_priv = value; }
    }
}

这是因为 C# 中的每个非指针类型都可以转换为 object,包括值类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-25
    • 2020-09-21
    • 2017-06-25
    • 1970-01-01
    • 2019-07-02
    • 1970-01-01
    • 2021-12-06
    相关资源
    最近更新 更多