n 个字段中最多有一个字段不为 null

At most one field out of n is not null

提问人:Yair Halberstadt 提问时间:8/23/2019 更新时间:8/23/2019 访问量:344

问:

我在一个类中有 n 个字段。最多允许设置其中一个,因此如果不是这种情况,我需要抛出异常。我知道 n 的值。

我可以用显而易见的方式做到这一点:

    if (field1 != null && (field2 != null || field3 != null ||...)
        || field2 != null && (field1 != null || field3 != null ||...)
        ...)
        throw new Exception();

但是,在字段数量中,它的长度是 n^2,我绝对不想为此写出来。10 个字段。

我看到建议通过反思来做到这一点。然而,虽然在你不知道 n 的值的情况下这是必要的,但我不禁认为这里一定有一种更简单的方法。

那么有没有办法检查 n 个值中最多是否有一个不是 null,而每个值只访问一次?

C# 算法 逻辑

评论

2赞 Lasse V. Karlsen 8/23/2019
计算 null/non-null 字段的数量并进行比较。或者仅使用集合类型来保存值(假设它们是相同的类型)。
2赞 Cee McSharpface 8/23/2019
重新考虑一下。使用数组或其他可枚举数据结构。或者每当遇到一个非 null 时递增一个值,一旦计数达到 2 就抛出。
0赞 Yair Halberstadt 8/23/2019
@dlatikay。有时数组是合适的。有时并非如此。

答:

0赞 Yair Halberstadt 8/23/2019 #1
public int NotNullToOne<T>(T val) => val is null ? 0 : 1;

if (NotNullToOne(field1) + NotNullToOne(field2) + NotNullToOne(field3) + ... > 1)
   throw new Exception();

如果值不为 null,则返回 NotNullToOne,如果值为 o,则返回 o。

如果多个值为 null,则所有 s 的总和将大于 1,因此将引发。NotNullToOne

评论

1赞 Lasse V. Karlsen 8/23/2019
在他的问题代码中,他检查了每个字段的组合,即。“9 个 ok 字段 + 1 个 null”,然后是“8 个 ok 字段 + 1 个 null + 1 个 ok 字段”,等等。这个计算 OK 字段并检查。
0赞 Yair Halberstadt 8/23/2019
这在字段数量上是线性的,而不是二次的。
0赞 MrSmith42 8/23/2019
这正是 Lasse Vågsæther Karlsen 在对您的问题的评论中提到的。
0赞 Yair Halberstadt 8/23/2019
@MrSmith42 是的。我碰巧想到了这一点并写下了这个答案,而他则写了那条评论。
2赞 mihai.luca 8/23/2019 #2

您可以有一个计数器来表示设置了多少个字段。

int numberOfSetFields = 0;

if (field1 != null)
    numberOfSetFields++;

if (field2 != null)
    numberOfSetFields++;

if (field3 != null)
    numberOfSetFields++;

...

if (numberOfSetFields > 1)
{
    throw new Exception();
}

或者,如果已设置至少 1 个以上的字段,则可以使用属性不再检查其他字段

int numberOfSetFields = 0;
int NumberOfSetFields 
{
    get { return numberOfSetFields; }
    set 
    {
        numberOfSetFields = value;
        if (numberOfSetFields > 1)
            throw new Exception();
    }
}

if (field1 != null)
    NumberOfSetFields++;

if (field2 != null)
    NumberOfSetFields++;

if (field3 != null)
    NumberOfSetFields++;

...

NumberOfSetFields 属性的 set 方法将检查是否至少有 1 个设置字段。这样,假设您有 10 个字段,并且设置了前 2 个字段,那么与我提到的第一种方法相比,其他 8 个字段将不再被检查。

1赞 Peter B 8/23/2019 #3

可以使用具有描述性名称的帮助程序方法,以便在调用端更清楚地显示您尝试检查的内容,如下所示:

public static bool MoreThanOneNotNull<T>(params T[] items)
{
    int count = 0;
    for (int i = 0; i < items.Length; i++)
        if (items[i] is object && ++count > 1)
            return true;
    return false;
}

用法:

if (MoreThanOneNotNull(field1, field2, field3, field4, ... ))
    // ...

评论

0赞 Yair Halberstadt 8/23/2019
我认为这样做的缺点是它不必要地分配了一个数组,但在许多情况下,这将是一个不成熟的优化,而且它肯定比我的解决方案更清晰。