理解C#泛型和可空值类型。返回null或可空值。

8

Suppose I have the following class:

public class GenericClass<T>
{
    public T Find()
    {
        //return T if found otherwise null or Nullable<T>
    }
}

我希望能够用classstruct来专门化我的类。但我遇到了这个问题: 如果T类型没有限制为struct,那么我不能返回Nullable<T>

我想提供一个实现我的Find方法,使其在T被专门化为classstruct时都能工作。 如果Find失败,我希望如果Tclass则返回null,否则返回Nullable<T>

这是否可以在不使用反射的情况下实现?如果可以,怎么实现?


如果返回类型是T,则不能返回T或Nullable<T>。 - fsimonazzi
3个回答

16
您可以返回default(T)
对于类来说,这将是null。 对于任何Nullable<T>,这将是一个没有值的Nullable<T>(有效地为null)。
话虽如此,如果您将其与struct一起使用而不是Nullable<T>作为类型,则default(T)将是结构体的默认值。
如果您想要使此功能在任何类或结构中都能统一工作,您可能需要返回两个值 - 您可以在此处以框架为灵感,使用TryXXX方法,例如:
public bool TryFind(out T)

如果找不到值,您可以使用 default(T) 并返回 false。这避免了需要使用可空类型。您也可以编写返回 Tuple<bool, T> 或类似的内容,以避免使用 out 参数,例如:

public Tuple<bool, T> Find()

最后一种可能的选择是将您的类变成非泛型,然后使用一对泛型方法:

class YourClass // Non generic
{
    public T FindReference<T>() where T : class
    {
         // ...
        return null;
    }

    public Nullable<T> FindValue<T>() where T : struct
    {
         // ...
         return default(T);
    }
}

请注意,您需要使用不同的名称,因为您不能仅基于返回类型来重载方法。

@Tejs。所以如果我需要将T专门用于数字类型,我是否被迫使用out参数来实现通用解决方案?如果T是int类型,那么默认返回什么? - Heisenbug
谢谢解释。我想我会使用输出参数解决方案。在C#中,使用输出参数进行此操作是常见模式吗? - Heisenbug
是的,如果可行的话,我建议将通用参数移到方法而不是类中。我尽量避免在我的参数上使用 out,因为它会使测试变得更加困难,并且因为我觉得它看起来很丑 =D - Tejs
@Tejs 我也采用了基于元组的方法(如果您想避免 out 参数,这种方法很有效)。 - Reed Copsey
@Tejs:我不认为我会重构我的类。我需要在类级别上使用通用参数(GenericClass实际上更加复杂,它是一个图形),而且我不想为方法使用不同的名称,因为这会将太多逻辑放在客户端类上。顺便说一下,我的问题只是出于好奇,我想了解这个主题。我的T可能永远不会是int,我可以继续使用默认方法。非常感谢你们两个。 - Heisenbug
显示剩余8条评论

2
我会使用以下解决方案:
public Boolean Find(out T result)
{
    // Pseudo code
    if (FindItem == true)
    {
        result = ItemFound;
        return true;
    }
    result = default(T);
    return false;
}

为什么?因为Nullable<T>只接受结构体,而上述方法支持类和结构体。


1
+1,这个习语通常被命名为TryXXXX,因此TryGetValueTryFind是合适的。 - user7116
@sixlettervariables 是的,你说得对。但我保留了海森堡使用的名称。 - Felix K.

0
如Reed所说,您可以返回default(T)
在我看来,这有一个重大的缺点:如果您的方法说明如果未找到该项则返回default(T),则您将失去返回值类型的默认值的能力(例如,对于Find<int>返回0可能通常是一个完全有效的返回值)。
我宁愿选择像这样的东西
public class GenericClass<T>
{
    public Option<T> Find()
    {
        //return Option.Some(item) if found otherwise Option.None<T>()
    }
}

public static class Option
{
    public static Option<T> None<T>()
    {
        return new Option<T>(default(T), false);
    }

    public static Option<T> Some<T>(T value)
    {
        return new Option<T>(value, true);
    }
}

public sealed class Option<T>
{
    private readonly T m_Value;
    private readonly bool m_HasValue;

    public void Option(T value, bool hasValue)
    {
        m_Value = value;
        m_HasValue = hasValue;
    }

    public bool HasValue 
    {
        get { return m_HasValue; }
    }        

    public T Value 
    {
        get { return m_Value; }
    }
}

1
这实际上是编写一种不受结构体限制的新形式的Nullable<T> - 现在,如果C#具有模式匹配,那么这将是正确的方法 ;) - Reed Copsey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接