public static IDictionary<TKey, TValue> ToDictionary<T, TKey, TValue, TDictionary>(this IEnumerable<T>? source, Func<T, TKey> keySelector, Func<T, TValue> elementSelector, Func<TDictionary>? factory = null) where TDictionary : IDictionary<TKey, TValue> where TKey : notnull {
var d1 = factory is null ? new Dictionary<TKey, TValue>() : (IDictionary<TKey, TValue>)factory(); // <-- WORKS!
var d2 = factory?.Invoke() ?? new Dictionary<TKey, TValue>(); // <-- CS0023: Operator '?' cannot be applied to operand of type 'TDictionary'
//return something;
}
In the code above, the complaint from the compiler seems erroneous or incomplete. The ? operator is applied to Func<...>, and _not_ to TDictionary. Both lines d1 = ... and d2 = ... should produce identical results.
Version Info
VS 2019 Community 16.8 Preview 6 (Latest)
.NET SDK (reflecting any global.json):
Version: 5.0.100-rc.2.20479.15
Commit: da7dfa8840
Runtime Environment:
OS Name: Windows
OS Version: 10.0.17134
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\5.0.100-rc.2.20479.15\
Host (useful for support):
Version: 5.0.0-rc.2.20475.5
Commit: c5a3f49c88
.NET SDKs installed:
5.0.100-rc.2.20479.15 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 5.0.0-rc.2.20475.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 5.0.0-rc.2.20475.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 5.0.0-rc.2.20475.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
The error is correct.
var d1 = factory is null ?
var d2 = factory?.Invoke() ??
The first line check factory for null. The second line check factory for null using ?. as well as the result of the Invoke method which is required to be nullable for ??. You can add class constraint if that's the intention but these two are not identical.
@alrz Could you clarify this more? I still cannot get the reasoning behind the error in this case (probably the OP too).
The documentation uses a similar example:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#thread-safe-delegate-invocation
What I would expect from factory?.Invoke() is:
factory is null, the result expression is null.The parameter factory is nullable, so ?. operator seems valid to me.
I also that if the error is correct, then it is not clear enough.
error CS0023: Operator '?' cannot be applied to operand of type 'TDictionary'
In the example, the operator ?. is being applied to factory which is of type Func<TDictionary>?. The error message indicates that the operator is being applied to a TDictionary. Not sure why, but this doesn't seem clear to me.
Note that a standalone factory?.Invoke(); compiles, however, when you're using the return value, the compiler tries to make a Nullable<T> if T is a value type or just use T if it's a reference type. but TDictionary is unconstrained, that's why the error is on the ?. operator.
@alrz A standalone factory?.Invoke(); didn't compile with me.
See SharpLab.
A simpler smaller repro:
https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxAN0QSwGYE8A+AAgEwCMAsAFBUDEAdgK4A2TEwTcABHHWx1VUIBmToVIA2UcU4BhKgG8qnZaJFjJhACycAsgB4AKgD4AFIQCshowH5O2CAGMYAewS5OAXk6MWASiUqipQqIZxoEAicACbSXvZOrrjWAHQAknRozgDWcCa+ANwBygC+ApTFQA==
You're still using the return value.
@alrz Oh I see. In that case, I think the error message should be more clear.
@alrz Thank you for your explanation. The bolded part makes sense. The problem is that I got an error squiggly precisely on ?. and the error message is not making sense there, because clearly the Func itself is nullable, hence the error should have been on the ?? part where it would make sense. So, I still think there is some kind of an issue here.
because clearly the
Funcitself is nullable
Using ?. to do a func call has two requirements:
See this code snippet:
https://sharplab.io/#v2:D4AQTAjAsAUCDMACciAaAeAKgPlgb1kSOSU0QDEB7SgCgEpEBebRAEwFMAzAQwFcAbAC4BuWAF9YsBMjCIAwvkLFpIACyIAslmw0MORJQBGAKzpKiBGMWuIAbtwBOiB+wDOAwUwMmA/ADoqWjpRK2IJGDEgA
This is currently of the shorthand of the language, but the compiler is following the spec correctly.
@huoyaoyuan Yep, I think the issue now is more about diagnostic clarity. While it makes sense to have a compile error, the current error message itself doesn't make a lot of sense in my opinion.