如何将T转换为Nullable<T>

5
我有一个通用的方法,需要保证该方法返回nullable<T>(例如源是 List<int>List<int?> 必须返回 int?),我想返回null而不是默认值(例如int的默认值为0。在这种情况下,我想返回null)。可以对类型参数添加约束(https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters),但采用这种方法,我必须拥有两个方法(其中一个带有struct约束,另一个带class约束)。
我的方法如下:
public static T? ElementOrNull<T>(List<T> source, int? index)
{
    if (source == null || index == null || source.Count <= index)
        return null;
    return source[index.Value];
}

我尝试使用 Convert.ChangeType() 和反射 (Activator.CreateInstance()Activator.CreateInstance<T?>()) 但是无法解决这个问题。

任何反馈都将不胜感激。谢谢!

注意:我查看了所有有关泛型和类型转换的StackOverflow问题,但它们没有帮助到我。


7
两个重载有什么问题吗?使用时不会注意到是两个,总会使用正确的那个。请注意,此处的“重载”指的是编程中的函数重载(overload),而非负荷过重的意思。 - Dejan Janjušević
它们不是相同的。一个是T?,它是Nullable<T>的简写,另一个是T。尽管它们不能共存,因为名称和输入参数是相同的。 - ProgrammingLlama
3
由于 int? 实际上是 System.Nullable<int>,所以您需要使用2种方法。如果 T 是一个类,则必须返回原始的 T,但如果 T 是一个结构体,则必须返回包装器 Nullable<T>。这无法在 .NET 的类型系统中表达。 - JL0PD
4
如果您允许在 List<T> 中包含 null (即 List<T?>),这两个方法将成为有效的重载。 - Theraot
很遗憾,@GSerg的方法并没有帮助我,但是“JL0PD”和“Theraot”的解决方案对我有用。 - Mohsen Koorani
显示剩余3条评论
2个回答

3

泛型限制不是方法签名的一部分,但编译器仍然需要知道T是值类型还是引用类型来将null转换为T并正确处理T?中的?。 但您可以通过可选的约束参数帮助编译器这样做:

public class ReqStruct<T> where T : struct
{
}

public class ReqClass<T> where T : class
{
}

public static T? ElementOrNull<T>(List<T> source, int? index, ReqStruct<T> req = null) where T : struct
{
    if (source == null || index == null || source.Count <= index)
        return null;
    return source[index.Value];
}

public static T? ElementOrNull<T>(List<T> source, int? index, ReqClass<T> req = null) where T : class
{
    if (source == null || index == null || source.Count <= index)
        return null;
    return source[index.Value];
}

由于参数是可选的,使用方式如下:

var intOrNull = MethodHolder.ElementOrNull(new List<int> { 1 }, 5); // var is int?
var objOrNull = MethodHolder.ElementOrNull(new List<object>(), 5); // var is object?

我之前想过这种方法,但是我不能更改方法的签名,即使这样做会添加可选参数。 - Mohsen Koorani
关于此事,请参见 https://dev59.com/1G_Xa4cB1Zd3GeqP2Y1g 和 https://codeblog.jonskeet.uk/2010/11/02/evil-code-overload-resolution-workaround/。 - Orace

3

我更喜欢使用自定义的选项/Maybe类型来处理这个问题。即,用一个带有通用值和布尔值的结构体来描述该值是否有效。也就是说,永远不要使用null,而应该对可选参数/返回值使用Maybe

public static Maybe<T> ElementOrNone<T>(List<T> source, Maybe<int> index)
{
    if (source == null || !index.HasValue || source.Count <= index.Value)
        return Maybe<T>.None;
    return source[index.Value];
}

尽管 Nullable<T> 和非空引用有助于避免空引用异常,但在编写通用代码时我发现它们存在不足,部分原因就是你所描述的问题。

使用自定义类型的优点是对值类型和引用类型具有一致的行为,并且可以允许各种扩展来使处理值更加容易,甚至可以使用linq查询语法组合多个值。缺点是这是一个自定义类型,需要学习。


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