避免泛型函数中的范围问题

时间:2015-07-22 00:15:58

标签: c# generics

我来自PHP和Javascript的Wild Wild West,您可以从函数中返回任何内容。虽然我不喜欢缺乏问责制,但我在努力保持代码“完美”方面也遇到了新的挑战。

我使用这个通用函数从列表中选择一个随机元素

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#中有很多疯狂的东西,我不知道,对于这个应用程序,我正在创建我非常,经常必须从列表中选择一个可以在其Count中不断递减的随机元素。还有更好的方法吗?

3 个答案:

答案 0 :(得分:8)

您拥有的选项是

return default(T)

这将是一个模棱两可的行为,因为它可能是列表的有效元素。

或者您可以按照自己的说法返回-1之类的内容,但这与您的代码完全相关。

或者您可以返回null,但这只能在T是可以为空的类型时才能完成。

在之前的所有情况下,如果来电者不知道这种情况,应用程序可能会继续使用无效值,从而导致未知后果

所以最好的选择可能是抛出异常:

throw new InvalidOperationException();

通过这种方法,您快速失败,并确保在调用者的意图之外不会发生任何意外情况。

备份此选项的一个原因。以Linq的扩展方法为例。如果您在空列表中拨打First()Single()Last(),则会收到InvalidOperationException消息&#34;序列中不包含任何元素&#34 ; 。为您的班级提供类似于框架类的行为&#39;总是一件好事。

由于阿列克谢·列文科夫在问题中的评论,我添加了旁注。随机生成并不是最好的方法。看看this question

第二方注释。您将函数声明为IList<T>的扩展方法(通过在第一个参数之前使用this来执行此操作)但是您将其称为静态辅助方法。扩展方法是一种语法糖,而不是这样做:

foo = Utilites.PickRandom(myList);

允许您这样做:

foo = myList.PickRandom();

有关扩展程序的更多信息,请访问here

答案 1 :(得分:0)

另一种选择是以下一对重载而不是原始重载。有了这些,调用者应该清楚,如果无法从列表中“挑选”,他们将提供默认随机值。

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];
}

注意:您应该考虑随机创建类的静态成员字段,而不是反复重新实例化它。请参阅此帖Correct method of a "static" Random.Next in C#?

的答案

答案 2 :(得分:0)

您拥有的另一个选择是使用<?= GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'home_team', 'away_team', 'line', 'over_under', 'home_fd', ], ]); ?> monad。它与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;
}

就个人而言,我最后用Maybe命名我的方法。

更像是这样:

var item = list.PickRandom();

if (item.HasValue) { ... }