避免泛型函数中超出范围的问题
本文关键字:范围 问题 中超 泛型 函数 | 更新日期: 2023-09-27 18:03:49
我来自PHP和Javascript的狂野西部,在那里你可以从函数返回任何东西。虽然我讨厌缺乏责任感,但在努力保持代码"完美"方面,我也面临着新的挑战。
我用这个泛型函数从列表中随机选取一个元素
public static T PickRandom<T>(this IList<T> list) {
Random random = new Random();
int rnd = random.Next(list.Count);
return list[rnd];
}
但是我不想在一个0值的列表上使用它。显然,我不能从这个函数中返回除T以外的任何值,比如false或-1。我当然可以这样做
if(myList.Count > 0)
foo = Utilites.PickRandom(myList);
然而,c#中有很多疯狂的事情我不知道,对于我正在创建的这个应用程序,我非常,非常经常必须从列表中选择一个随机元素,该元素可以在其计数中不断递减。有没有更好的办法?
你的选择是
return default(T)
这将是一个模棱两可的行为,因为它可能是列表的一个有效元素。
或者你可以像你说的那样返回-1
,但这与你的代码是耦合的。
或者您可以返回null
,但这只能在T
是可空类型的情况下完成,当然。
在前面的所有情况下,如果调用者不知道这种情况,应用程序可能会继续使用无效的值,导致未知的结果。
所以最好的选择可能是抛出一个异常:throw new InvalidOperationException();
使用这种方法,快速失败,并确保没有超出调用者意图的意外发生。
备份此选项的另一个原因。以Linq的扩展方法为例。如果您在空列表上调用First()
, Single()
或Last()
,您将获得带有消息"序列不包含元素"的InvalidOperationException
。给你的类一个类似于框架类的行为总是一件好事。
感谢Alexei Levenkov在问题中的评论,我添加了一个旁注。随机生成并不是最好的方法。看一下这个问题。
第二个边注。您正在声明您的函数作为IList<T>
的扩展方法(您通过在第一个参数之前使用this
来做到这一点),但随后您像调用静态助手方法一样调用它。扩展方法是一个语法糖,而不是这样做:
foo = Utilites.PickRandom(myList);
允许您这样做:
foo = myList.PickRandom();
关于扩展方法的更多信息可以在这里找到
另一种替代方法是下面这对重载来代替原来的重载。有了这些,调用者应该清楚地知道,如果不能从列表中"选择"一个值,它们将提供一个默认的随机值。
public static T PickRandomOrReturnDefault<T>(this IList<T> list, T defaultRandomValue)
{
if (list == null || list.Count == 0) return defaultRandomValue;
Random random = new Random();
int rnd = random.Next(list.Count);
return list[rnd];
}
public static T PickRandomOrReturnDefault<T>(this IList<T> list, Func<T> createRandomValue)
{
if (list == null || list.Count == 0) return createRandomValue();
Random random = new Random();
int rnd = random.Next(list.Count);
return list[rnd];
}
注意:您可能应该考虑将random作为类的静态成员字段,而不是一次又一次地重新实例化它。查看这篇文章的答案:一个"静态"的正确方法随机的。c#的下一步?
另一个选择是使用Maybe<T>
单子。它与Nullable<T>
非常相似,但可以使用引用类型。
public class Maybe<T>
{
public readonly static Maybe<T> Nothing = new Maybe<T>();
public T Value { get; private set; }
public bool HasValue { get; private set; }
public Maybe()
{
HasValue = false;
}
public Maybe(T value)
{
Value = value;
HasValue = true;
}
public static implicit operator Maybe<T>(T v)
{
return v.ToMaybe();
}
}
你的代码可以像这样:
private static Random random = new Random();
public static Maybe<T> PickRandom<T>(this IList<T> list)
{
var result = Maybe<T>.Nothing;
if (list.Any())
{
result = list[random.Next(list.Count)].ToMaybe();
}
return result;
}
你可以这样使用:
var item = list.PickRandom();
if (item.HasValue) { ... }
就我个人而言,我将maybe方法命名为maybe。
应该是这样的:
var itemMaybe = list.PickRandomMaybe();
if (itemMaybe.HasValue) { ... }