可空值元组问题
Nullable Value Tuples issue
我正在尝试更新我的代码以使用新的值元组,总的来说它使它更具可读性,所以我喜欢它。
但是,我 运行 遇到了使用可空值的问题。特别是我有必须是 null
的集合条目或在某些条件下 return null
值的函数。例如:
internal static TValue SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) =>
list.TryGetValue(key, out TValue ret) ? ret : default(TValue);
这适用于普通的 Tuple<>
对象,因为它们是 类,但值类型永远不会 return null
,这打破了这一点。
我试过类似下面的方法,但重载分辨率并不能解决这种情况,它只会给出编译错误:
internal static TValue SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) where TValue : class =>
list.TryGetValue(key, out TValue ret) ? ret : default(TValue);
internal static TValue? SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) where TValue : struct =>
list.TryGetValue(key, out TValue ret) ? ret : new TValue?();
如果没有提供执行此操作的新方法,是否有 clean 方法将其写入 return 可空类型,而 TValue
是struct
?我强调干净这个词,因为我显然可以围绕它进行编码,但我想要一个语义解决方案而不是蛮力方法。
请记住 FirstOrDefault()
Linq 方法也会发生同样的问题。它不会 return null
,这使得它在这种情况下相当无用。如果可能的话,我也想要一个解决这个问题的解决方案。
编辑:请记住 Linq 问题。 这不是另一个 post 的副本,因为他们的要求更灵活。如果可能的话,我想让 Linq 像往常一样处理值元组。
至于link的问题,很容易解决:
IEnumerable<int> values = ...
int? fod = values.Select(i => (int?)i).FirstOrDefault();
请注意,这不仅适用于 IEnumerable<T>
,还适用于 IQueryable<T>
。
至于更笼统的问题,Generic constraints, where T : struct and where T : class
确实回答了
不清楚您的问题与您的示例有何关联。
您可以像这样对任何类型(值类型或引用类型元素)的 IEnumerable<>
进行扩展:
public static bool TryGetFirst<T>(this IEnumerable<T> source, out T first)
{
var oneOrEmpty = source.Take(1).ToList();
if (oneOrEmpty.Count == 1)
{
first = oneOrEmpty[0];
return true;
}
first = default(T);
return false;
}
或者稍微好一点的性能(我认为):
public static bool TryGetFirst<T>(this IEnumerable<T> source, out T first)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
first = e.Current;
return true;
}
}
first = default(T);
return false;
}
在这里您使用 returned bool
来查看是否有第一个元素,然后您检查 out
参数以查看在这种情况下第一个元素是什么。
如果序列可能包含 null
值(作为其第一个成员),这对于引用类型的序列也很有用。
否则,仅对于值类型,您可以从 Kris Vandermotten 的回答中窃取技巧并将其放入扩展方法中:
public static T? TryGetFirst<T>(this IEnumerable<T> source) where T : struct
{
return source.Select(t => (T?)t).FirstOrDefault();
}
在这种情况下,null
的 return 值表示没有第一个元素。
我正在尝试更新我的代码以使用新的值元组,总的来说它使它更具可读性,所以我喜欢它。
但是,我 运行 遇到了使用可空值的问题。特别是我有必须是 null
的集合条目或在某些条件下 return null
值的函数。例如:
internal static TValue SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) =>
list.TryGetValue(key, out TValue ret) ? ret : default(TValue);
这适用于普通的 Tuple<>
对象,因为它们是 类,但值类型永远不会 return null
,这打破了这一点。
我试过类似下面的方法,但重载分辨率并不能解决这种情况,它只会给出编译错误:
internal static TValue SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) where TValue : class =>
list.TryGetValue(key, out TValue ret) ? ret : default(TValue);
internal static TValue? SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) where TValue : struct =>
list.TryGetValue(key, out TValue ret) ? ret : new TValue?();
如果没有提供执行此操作的新方法,是否有 clean 方法将其写入 return 可空类型,而 TValue
是struct
?我强调干净这个词,因为我显然可以围绕它进行编码,但我想要一个语义解决方案而不是蛮力方法。
请记住 FirstOrDefault()
Linq 方法也会发生同样的问题。它不会 return null
,这使得它在这种情况下相当无用。如果可能的话,我也想要一个解决这个问题的解决方案。
编辑:请记住 Linq 问题。 这不是另一个 post 的副本,因为他们的要求更灵活。如果可能的话,我想让 Linq 像往常一样处理值元组。
至于link的问题,很容易解决:
IEnumerable<int> values = ...
int? fod = values.Select(i => (int?)i).FirstOrDefault();
请注意,这不仅适用于 IEnumerable<T>
,还适用于 IQueryable<T>
。
至于更笼统的问题,Generic constraints, where T : struct and where T : class
确实回答了不清楚您的问题与您的示例有何关联。
您可以像这样对任何类型(值类型或引用类型元素)的 IEnumerable<>
进行扩展:
public static bool TryGetFirst<T>(this IEnumerable<T> source, out T first)
{
var oneOrEmpty = source.Take(1).ToList();
if (oneOrEmpty.Count == 1)
{
first = oneOrEmpty[0];
return true;
}
first = default(T);
return false;
}
或者稍微好一点的性能(我认为):
public static bool TryGetFirst<T>(this IEnumerable<T> source, out T first)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
first = e.Current;
return true;
}
}
first = default(T);
return false;
}
在这里您使用 returned bool
来查看是否有第一个元素,然后您检查 out
参数以查看在这种情况下第一个元素是什么。
如果序列可能包含 null
值(作为其第一个成员),这对于引用类型的序列也很有用。
否则,仅对于值类型,您可以从 Kris Vandermotten 的回答中窃取技巧并将其放入扩展方法中:
public static T? TryGetFirst<T>(this IEnumerable<T> source) where T : struct
{
return source.Select(t => (T?)t).FirstOrDefault();
}
在这种情况下,null
的 return 值表示没有第一个元素。