是否有类似于NotNullIfNotNullAttribute的东西,适用于IEnumerable返回值?

huangapple go评论80阅读模式
英文:

Is there something similar to NotNullIfNotNullAttribute for IEnumerable return values?

问题

I know that I can use NotNullIfNotNullAttribute to specify that the return value of my method may be null if, and only if, an input is null:

我知道可以使用 NotNullIfNotNullAttribute 来指定我的方法的返回值可能为 null,仅当输入为 null 时:

[return: NotNullIfNotNull(nameof(defaultValue))]
public T? GetValueOrDefault<T>(T? defaultValue)
{ 
    return GetValueFromSomewhere<T>() ?? defaultValue;
}

Is there something similar for methods returning IEnumerable<T>? I'd like to tell the compiler that all elements of the returned IEnumerable are non-null if the input parameter is non-null.

对于返回 IEnumerable<T> 的方法是否有类似的东西?我想告诉编译器,如果输入参数非 null,返回的 IEnumerable 中的所有 元素 都是非 null。

[return: ???]
public IEnumerable<T?> GetValuesOrDefault<T>(T? defaultValue)
{
    return GetValuesFromSomewhere<T>().Select(x => x ?? defaultValue);
}

Here's a MCVE (fiddle) to play around with:

以下是一个最小可复现的示例(fiddle)可供测试:

#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
                    
public class Program
{
    public static void Main()
    {
        string? s1 = GetValueOrDefault<string>(null);
        string s2 = GetValueOrDefault<string>("");   // works, C# realizes that the return value can't be null
		
        IEnumerable<string?> e1 = GetValuesOrDefault<string>(null);
        IEnumerable<string> e2 = GetValuesOrDefault<string>("");   // I want to get rid of the warning here
    }
    
    [return: NotNullIfNotNull(nameof(defaultValue))]
    public static T? GetValueOrDefault<T>(T? defaultValue)
    { 
        return GetValueFromSomewhere<T>() ?? defaultValue;
    }
    
    public static IEnumerable<T?> GetValuesOrDefault<T>(T? defaultValue)
    {
        return GetValuesFromSomewhere<T>().Select(x => x ?? defaultValue);
    }
    
    // Dummy placeholders for my MCVE. My real methods read values from my DB repository.
    private static T? GetValueFromSomewhere<T>() => default(T);
    private static IEnumerable<T?> GetValuesFromSomewhere<T>() => new T?[] { default(T) };
}
英文:

(This is a question about C# nullable reference types and generics.)

I know that I can use NotNullIfNotNullAttribute to specify that the return value of my method may be null if, and only if, an input is null:

[return: NotNullIfNotNull(nameof(defaultValue))]
public T? GetValueOrDefault&lt;T&gt;(T? defaultValue)
{ 
    return GetValueFromSomewhere&lt;T&gt;() ?? defaultValue;
}

Is there something similar for methods returning IEnumerable&lt;T&gt;? I'd like to tell the compiler that all elements of the returned IEnumerable are non-null if the input parameter is non-null.

[return: ???]
public IEnumerable&lt;T?&gt; GetValuesOrDefault&lt;T&gt;(T? defaultValue)
{
    return GetValuesFromSomewhere&lt;T&gt;().Select(x =&gt; x ?? defaultValue);
}

Here's a MCVE (fiddle) to play around with:

#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
                    
public class Program
{
    public static void Main()
    {
        string? s1 = GetValueOrDefault&lt;string&gt;(null);
        string s2 = GetValueOrDefault&lt;string&gt;(&quot;&quot;);   // works, C# realizes that the return value can&#39;t be null
		
		IEnumerable&lt;string?&gt; e1 = GetValuesOrDefault&lt;string&gt;(null);
		IEnumerable&lt;string&gt; e2 = GetValuesOrDefault&lt;string&gt;(&quot;&quot;);   // I want to get rid of the warning here
    }
	
	[return: NotNullIfNotNull(nameof(defaultValue))]
	public static T? GetValueOrDefault&lt;T&gt;(T? defaultValue)
	{ 
	    return GetValueFromSomewhere&lt;T&gt;() ?? defaultValue;
	}
	
	public static IEnumerable&lt;T?&gt; GetValuesOrDefault&lt;T&gt;(T? defaultValue)
	{
	    return GetValuesFromSomewhere&lt;T&gt;().Select(x =&gt; x ?? defaultValue);
	}
	
	// Dummy placeholders for my MCVE. My real methods read values from my DB repository.
	private static T? GetValueFromSomewhere&lt;T&gt;() =&gt; default(T);
	private static IEnumerable&lt;T?&gt; GetValuesFromSomewhere&lt;T&gt;() =&gt; new T?[] { default(T) };
}

答案1

得分: 2

我认为你想要的已经是可能的。

在这种情况下,我们想要修改其nullability的输入和输出都是类型T,这是封闭方法上的类型参数。在这里,我们不需要使用[NotNullIfNotNull] — 我们可以使用方法类型参数推断来实现我们想要的效果。SharpLab

// ok
IEnumerable&lt;string&gt; a = GetValuesOrDefault(new[] { &quot;a&quot;, null }, defaultValue: &quot;b&quot;);
IEnumerable&lt;string&gt; b = GetValuesOrDefault(new[] { &quot;a&quot;, &quot;b&quot; }, defaultValue: &quot;b&quot;);

// warning: Nullability of reference types in value of type &#39;IEnumerable&lt;string?&gt;&#39; doesn&#39;t match target type &#39;IEnumerable&lt;string&gt;&#39;.
IEnumerable&lt;string&gt; c = GetValuesOrDefault(new[] { &quot;a&quot;, null }, defaultValue: null);

IEnumerable&lt;T&gt; GetValuesOrDefault&lt;T&gt;(IEnumerable&lt;T?&gt; valuesFromSomewhere, T defaultValue)
{
    return valuesFromSomewhere.Select(x =&gt; x ?? defaultValue);
}

这给了我们在这里想要的控制点:如果输入的defaultValue是非可空的,那么结果的IEnumerable 也具有非可空的类型参数。如果输入的defaultValue 可能为 null,那么结果的IEnumerable 具有可空的类型参数。

英文:

I think what you want is already possible.

In this case, both the input and output whose nullability we want to modify are of type T, a type parameter on the enclosing method. We don't need [NotNullIfNotNull] here — we can just use method type argument inference to achieve what we want. SharpLab.

// ok
IEnumerable&lt;string&gt; a = GetValuesOrDefault(new[] { &quot;a&quot;, null }, defaultValue: &quot;b&quot;);
IEnumerable&lt;string&gt; b = GetValuesOrDefault(new[] { &quot;a&quot;, &quot;b&quot; }, defaultValue: &quot;b&quot;);

// warning: Nullability of reference types in value of type &#39;IEnumerable&lt;string?&gt;&#39; doesn&#39;t match target type &#39;IEnumerable&lt;string&gt;&#39;.
IEnumerable&lt;string&gt; c = GetValuesOrDefault(new[] { &quot;a&quot;, null }, defaultValue: null);


IEnumerable&lt;T&gt; GetValuesOrDefault&lt;T&gt;(IEnumerable&lt;T?&gt; valuesFromSomewhere, T defaultValue)
{
    return valuesFromSomewhere.Select(x =&gt; x ?? defaultValue);
}

This gives us the point of control we want here: If the defaultValue going in is non-nullable, the result IEnumerable has a non-nullable type argument. If the defaultValue going in may be null, the result IEnumerable has a nullable type argument.

答案2

得分: 1

不幸的是,目前没有这样的功能(至少通过属性)。同样的问题也出现在任务中,可以在这里(或这里)找到相关讨论,但根据评论,目前似乎只考虑了特定于任务和async-await情况(也请参考这个讨论)。

英文:

No, sadly but currently there is no such thing (at least via attributes). There same issue can be observed with tasks, which is discussed here (or this one), but based on comments it seems that at the moment only task-specific and async-await case are taken into consideration (see also this discussion).

huangapple
  • 本文由 发表于 2023年3月4日 03:25:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75631138.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定