【问题标题】:Mixing expression-bodied with throw for multiple parameters为多个参数混合表达式体与 throw
【发布时间】:2020-03-09 13:24:05
【问题描述】:

这可能是一件容易的事。但如果可能的话,我无法解决这个问题。

因为我们可以将表达式体成员应用于构造函数,并且由于 throw 表达式可用于表达式体成员,所以我们可以简化以下代码

public class Foo
{
    public string ProA { get; set; }
    public Foo(string proa)
    {
        if (proa == null)
        {
            throw new ArgumentNullException("Invalid proa value");
        }
        ProA = proa;
    }
}

上面的代码可以简化为:

public class Foo
{
    public string ProA { get; set; }
    public Foo(string proa) => ProA = proa ?? 
        throw new ArgumentNullException("Invalid proa value");
}

问题: 就我而言,我有多个参数(在参数化构造函数中)要在构造时解析,如果有人可以帮助我在构造函数的表达式体成员以及在 null 的情况下抛出表达式

public class Bar
{
    public string ProA { get; set; }
    public string ProB { get; set; }
    public Bar(string proa, string prob)
    {
        if (proa == null)
            throw new ArgumentNullException("Invalid proa value");
        else if (prob == null)
            throw new ArgumentNullException("Invalid prob value");

        ProA = proa;
        ProB = prob;
    }
}

【问题讨论】:

  • 您可以删除最后一个else 语句,因为所有其他情况都会引发异常。此外,第一个无效参数通常是引发异常的原因,因此您也可以删除第一个 if 语句。
  • @RufusL,你说得对,我可以进一步简化代码。然而,这个想法是为了展示当前的代码库。无论如何,多余的 else 被删除。谢谢
  • 另外,我不相信你可以这样使用nameof——它需要一个变量、类型或成员;不是字符串。
  • @RufusL,感谢您发现这一点。非常感谢。

标签: c# c#-7.0


【解决方案1】:

问题: 就我而言,我有多个参数(在参数化构造函数中)要在构造时解析,如果有人可以帮助我在构造函数的表达式体成员以及在 null 的情况下抛出表达式

表达式体成员在 C# 7.0 中得到扩展,现在包括将它们与构造函数一起使用。该方法由一个单个表达式组成,该表达式返回一个类型与该方法的返回类型匹配的值,或者,对于返回 void 的方法,它执行一些操作

让我们看一下您的第一个简化代码块...

public class Foo
{
    public string ProA { get; set; }
    public Foo(string proa) => ProA = proa ?? 
        throw new ArgumentNullException("Invalid proa value");
}

这里使用表达式体就可以了,只包含one expression,所以它是有效的。

public Foo(string proa) => ProA = proa ?? 
            throw new ArgumentNullException("Invalid proa value");

当你有多个参数时,问题就出现了,多个参数需要处理,而不是有主体声明,表达式主体应该派上用场并且可以正常工作。

这是一种方法,同时使其有效。

public Bar(string proa, string prob) => 
            (ProA, ProB) = (proa ?? throw new ArgumentNullException("Invalid proa value"),
            prob ?? throw new ArgumentNullException("Invalid prob value"));

我在这里所做的基本上是创建一个ValueTuple Structdeconstructing,它可以让您在一次操作中解包一个元组中的所有项目。

可以做的另一个选项,但更多的工作是调用类中的例程来在构造时设置类。不过,走这条路会比处理常规身体的变化更麻烦。

参考资料:

Expression-bodied Members

【讨论】:

  • 非常感谢您的宝贵时间。我同意将 ValueTuple 及其解构工作作为解决我的问题的替代方法。感谢您的努力。
  • 我可能是唯一一个不喜欢所有语法糖的人,但我发现 OP 问题中的最后一个代码块是最易读的(这不是批评完全没有这个答案 - 它完全解决了所要求的问题)。
【解决方案2】:

[it] 如果有人可以帮助我在构造函数的表达式主体成员的帮助下简化以下代码以及在 null 的情况下抛出表达式,那将非常有帮助

“简化”往往是旁观者的看法,我认为在这里尤其如此。我还认为您将两个不同的概念混为一谈:表达式中的 throw 和 lambda 表达式方法实现。

如果我们只关注前者,您的构造函数可以大大简化(在我看来),只需更改表达式并删除显式的 if 语句:

public class Bar
{
    public string ProA { get; set; }
    public string ProB { get; set; }
    public Bar(string proa, string prob)
    {
        ProA = proa ?? throw new ArgumentNullException("Invalid proa value");
        ProB = prob ?? throw new ArgumentNullException("Invalid prob value");
    }
}

请务必注意,逻辑流程存在细微变化。 IE。参数验证仅在分配每个属性时进行,而不是在任何属性分配之前进行。但可以肯定的是,在这样的课程中,这完全无关紧要。

使用 lambda 表达式方法可以做类似的事情,但需要注意的是(即赋值和验证混合在一起):

public class Bar
{
    public string ProA { get; set; }
    public string ProB { get; set; }
    public Bar(string proa, string prob) : this(prob) => ProA = proa ?? throw new ArgumentNullException("Invalid proa value");
    private Bar(string prob) => ProB = prob ?? throw new ArgumentNullException("Invalid prob value");
}

换句话说,为您要分配的每个参数添加一个新的构造函数,为该构造函数提供一个 lambda 表达式来分配该参数,并让构造函数将剩余参数的分配委托给下一个构造函数(具有更多参数/属性,您可以看到参数列表每次调用都会缩短一个参数)。

我个人并不认为这是一个真正的改进。是的,代码更紧凑。但我不确定它实际上是否更具可读性。而且在一个重要的意义上,“简单”和“可读”是相同的,或者至少是密切相关的。

【讨论】:

  • 我完全同意你的简单概念。由于有不止一个重载的构造函数,我不能采纳你关于构造函数链接的建议。此外,空合并运算符是我已经考虑过的。但是,在我的情况下(就我个人而言),将表达式体成员引入构造函数可能是一种更简洁的方法。尽管您的提案没有回答我的问题,但我绝对赞成它,因为它为整个讨论增加了价值。非常感谢您的时间和精力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多