可空值元组问题

时间:2017-03-10 18:10:05

标签: c# nullable c#-7.0

我试图更新我的代码以使用新的值元组,总体来说它使它更具可读性,所以我喜欢它。

但是,我遇到了使用可空值的问题。特别是我有一些必须是null的集合条目或在某些条件下返回null值的函数。例如:

internal static TValue SafeTryGet<TKey, TValue>(this IDictionary<TKey, TValue> list, TKey key) =>
        list.TryGetValue(key, out TValue ret) ? ret : default(TValue);

这适用于普通Tuple<>对象,因为它们是类,但值类型永远不会返回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?();

如果没有提供一种新的方法来实现这一点,那么当TValuestruct时,是否有干净写入此方法以返回可以为空的类型?我强调清洁这个词,因为我可以明显地编写它,但我想要解决问题的语义,而不是强力方法。

请记住FirstOrDefault() Linq方法会出现同样的问题。它不会返回null,这使得它在这种情况下无用。我想要一个能够解决这个问题的解决方案。

编辑:请记住Linq问题。 这不是其他帖子的重复,因为他们的要求更灵活。如果可能的话,我想让Linq正常使用值元组。

2 个答案:

答案 0 :(得分:1)

关于链接问题,这很容易解决:

IEnumerable<int> values = ...

int? fod = values.Select(i => (int?)i).FirstOrDefault();

请注意,这不仅适用于IEnumerable<T>,也适用于IQueryable<T>

至于更一般的问题,Generic constraints, where T : struct and where T : class

确实回答了这个问题

答案 1 :(得分:0)

目前尚不清楚您的问题与您的示例有何关联。

您可以对任何类型的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;
}

在这里,您使用返回的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表示没有第一个元素。