在单个C#泛型方法中返回nullable和null?

Jam*_*hon 11 c# generics nullable

在C#泛型方法中是否可以返回对象类型或Nullable类型?

例如,如果我有一个安全的索引访问器List,我想返回一个值,我可以稍后检查== null或使用或.HasValue().

我目前有以下两种方法:

static T? SafeGet<T>(List<T> list, int index) where T : struct 
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}

static T SafeGetObj<T>(List<T> list, int index) where T : class
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}
Run Code Online (Sandbox Code Playgroud)

如果我尝试将方法组合到一个方法中.

static T SafeGetTest<T>(List<T> list, int index)
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}
Run Code Online (Sandbox Code Playgroud)

我收到编译错误:

无法将null转换为类型参数'T',因为它可能是不可为空的值类型.考虑使用'default(T)'代替.

但我不想使用,default(T)因为在原语的情况下0,这是一个默认值int,是一个可能的实际值,我需要区分不可用的值.

这些方法是否可以组合成一个方法?

(为了记录我使用.NET 3.0,虽然我对更现代的C#可以做什么感兴趣,但我个人只能使用3.0中的答案)

Ron*_*yer 6

还有一个选项,你可能没有考虑过......

public static bool TrySafeGet<T>(IList<T> list, int index, out T value)
{
    value = default(T);

    if (list == null || index < 0 || index >= list.Count)
    {    
        return false;
    }

    value = list[index];
    return true;
}
Run Code Online (Sandbox Code Playgroud)

这可以让你做这样的事情:

int value = 0;
if (!TrySafeGet(myIntList, 0, out value))
{
    //Error handling here
}
else
{
    //value is a valid value here
}
Run Code Online (Sandbox Code Playgroud)

而在顶部,它与TryXXX许多其他类型集合兼容,甚至与转换/解析API 兼容.它也很明显该函数来自方法的名称,它"尝试"获取值,如果不能,则返回false.


Me.*_*ame 5

不完全是你想要的,但可能的解决方法是返回一个元组(或其他包装类):

    static Tuple<T> SafeGetObj<T>(List<T> list, int index) 
    {
        if (list == null  || index < 0 || index >= list.Count)
        {
            return null;
        }

        return Tuple.Create(list[index]);
    }
Run Code Online (Sandbox Code Playgroud)

Null总是意味着不能获得任何值,单个元组本身就意味着一个值(即使值本身可以为null).

在vs2015中,您可以?.在调用时使用符号:var val = SafeGetObj(somedoublelist, 0)?.Item1; 当然,您可以创建自己的通用包装器而不是Tuple.

如上所述,并不完全是最优的,但它是一个可行的工作,并且具有能够看到不是有效选择和空元素之间的区别的额外好处.


自定义包装器实现的示例:

    struct IndexValue<T>
    {
        T value;
        public bool Succes;
        public T Value
        {
            get
            {
                if (Succes) return value;
                throw new Exception("Value could not be obtained");
            }
        }

        public IndexValue(T Value)
        {
            Succes = true;
            value = Value;
        }

        public static implicit operator T(IndexValue<T> v) { return v.Value; }
    }

    static IndexValue<T> SafeGetObj<T>(List<T> list, int index) 
    {
        if (list == null || index < 0 || index >= list.Count)
        {
            return new IndexValue<T>();
        }

        return new IndexValue<T>(list[index]);
    }
Run Code Online (Sandbox Code Playgroud)


Aro*_*ron 5

你可以做类似但不同的事情.结果几乎相同.这依赖于重载和方法解析规则.

private static T? SafeGetStruct<T>(IList<T> list, int index) where T : struct 
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}

public static T SafeGet<T>(IList<T> list, int index) where T : class
{
    if (list == null || index < 0 || index >= list.Count)
    {
        return null;
    }

    return list[index];
}

public static int? SafeGet(IList<int> list, int index)
{
    return SafeGetStruct(list, index);
}
public static long? SafeGet(IList<long> list, int index)
{
    return SafeGetStruct(list, index);
}

etc...
Run Code Online (Sandbox Code Playgroud)

不太对劲?但它的确有效.

然后我会在T4模板中包装整个内容以减少代码编写量.

编辑:我的OCD让我使用IList而不是List.