简体   繁体   中英

Understanding C# generics and Nullable value type. Return null or nullable

Suppose I have the following class:

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

Somewhere I'd like to specialize my class using T with a class , other times with a struct . I'm facing this issue : I can't return a Nullable<T> if T type isn't restricted to be a struct .

I would like to provide an implementation of my Find method that works if T is specialized with both a class or a struct . In case Find fails , I'd like to return null if T is a class otherwise Nullable<T> .

Is that possible without using reflection? If yes how?

You can return default(T) .

For a class, this will be null . For any Nullable<T> , this will be a Nullable<T> without a value (effectively null ).

That being said, if you use this with a struct and not a Nullable<T> as the type, default(T) will be the default value of the struct.


If you want to make this work uniformly for any class or struct, you would likely need to return two values - you could use the framework as inspiration here, and have a TryXXX method, ie:

public bool TryFind(out T)

You could then use default(T) when the value isn't found, and return false. This avoids the need for nullable types. You could also write this returning a Tuple<bool, T> or similar, if you wanted to avoid the out parameter, ie:

public Tuple<bool, T> Find()

A final option, potentially, would be to make your class non-generic, then use a pair of generic methods:

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

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

Note that you need distinct names, since you can't have an overloaded method purely based on the return type.

I would use the following solution:

public Boolean Find(out T result)
{
    // Pseudo code
    if (FindItem == true)
    {
        result = ItemFound;
        return true;
    }
    result = default(T);
    return false;
}

Why? Because Nullable<T> only accepts a struct and the method above supports both, classes and structs.

As Reed said you could return default(T) .

In my opinion this has one big disadvantage: If your method states that default(T) is returned if the item was not found, you lose the ability to return the default values for value types (eg returning 0 for Find<int> might generally be a perfectly valid return value).

I would rather go for something like this

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM