【问题标题】:Use custom attribute to skip body method使用自定义属性跳过正文方法
【发布时间】:2018-09-23 19:22:06
【问题描述】:

我有以下功能:

public void Test(string testString)
{
     //Do Stuff
}

在我的代码中的某些点,我必须反复检查参数是否为空字符串/null/空格以跳过正文方法。到目前为止,我这样做的常用方法如下:

public void Test(string testString)
{
     if(!string.IsNullOrWhiteSpace(testString))
     {
         //Do Stuff only if string has text in it.
     }
}

或者

public void Test(string testString)
{
     if(string.IsNullOrWhiteSpace(testString)) { return; }
     //Do Stuff only if string has text in it.
}

有没有办法创建一个自定义属性来检查函数的参数是否为空等,以跳过该方法?我有一些经验(基本的东西),有自定义属性,但我想不出一种方法让属性跳过方法体。

实现的理想最终产品如下:

[SkipIfEmptyParameter]
public void Test(string testString)
{
     //Do Stuff only if string has text in it.
}

当然,如果无法实现属性,任何有助于最小化重复代码的建议都是受欢迎的。

编辑:我要解决的问题示例。

我有以下方法。我从 Microsoft 测试管理器中获得了我们的测试场景所期望的一些参数(值应该是什么)。有一个 SharedStep 实现可以断言用户的信息:

public void AssertUser(UserDTO expectedUserInfo)
{
    VerifyUserName(expectedUserInfo.name);
    VerifyUserSurname(expectedUserInfo.surname);
    VerifyUserAge(expectedUserInfo.age);
    VerifyUserHeight(expectedUserInfo.height);
}

private void VerifyUserName(string name)
{
     //If the string parameter is empty, means the MTM scenario does not
     //want to validate the user's name at this point, so skip the
     //verification below.
     if(string.IsNullOrWhiteSpace(testString)) { return; }

     //Do Stuff only if string has text in it.
}

private void VerifyUserSurname(string surname)
{
     //If the string parameter is empty, means the MTM scenario does not
     //want to validate the user's surname at this point, so skip the
     //verification below.
     if(string.IsNullOrWhiteSpace(testString)) { return; }
     //Do Stuff only if string has text in it.
}

private void VerifyUserAge(string age)
{
     //If the string parameter is empty, means the MTM scenario does not
     //want to validate the user's age at this point, so skip the
     //verification below.
     if(string.IsNullOrWhiteSpace(testString)) { return; }
     //Do Stuff only if string has text in it.
}

private void VerifyUserHeight(string height)
{
     //If the string parameter is empty, means the MTM scenario does not
     //want to validate the user's height at this point, so skip the
     //verification below.
     if(string.IsNullOrWhiteSpace(testString)) { return; }
     //Do Stuff only if string has text in it.
}

“Do Stuff”包含处理 WebElements 的 Selenium 实现,并且可能很耗时,因此如果我们不想验证该特定值,我们只需跳过整个方法。

现在,在将场景创建到 Microsoft 测试管理器时,共享步骤允许测试人员决定将验证页面的哪些元素。如果某些参数为空,则代码只是跳过块并转到用户想要的 w/e 验证(仍然,实现是针对用户拥有的每个信息,但我们只是为每个要测试的参数分配值,并且每个没有值的参数,只是跳过它的方法体)。

问题是,如果我想改变跳过方法的条件,我将不得不去每个方法并手动改变IF语句。因此,为什么我认为为每个验证信息的方法设置一个属性可能是个好主意。

附:我说的是数百种一开始就具有 IF 实现的方法。

【问题讨论】:

  • 真的值得吗?检查参数和返回代码不多,表达的意图很明确。
  • 谁调用这个方法以及如何调用?
  • 这听起来像是 XY 问题。你的代码试图解决什么问题?
  • 在 c# 中对于此类任务没有正确的决定,因为知道它不会执行而调用方法是毫无意义的。例如,如果您的方法是 API 的一部分,您可以在调用层中实现此逻辑,这就是为什么我要问谁调用了此方法。在 C# 中,您可以使用代码契约进行验证 docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/…
  • @Sefe 没有实际的“问题”,就问题的定义而言,代码原样工作正常,我试图看看是否有办法将所有条件都放在一个如果需要,可以全局更改它。上述条件适用于使用微软测试管理器获取参数的 MSTest,但最近,我们团队决定,如果字符串也是“”,我们也必须跳过方法体,而不是空字符串。所以我正在寻找是否有办法将 IF 放在一个位置,所以将来,如果需要,我们只需更改那行代码。而不是 100 多个 IF 语句。

标签: c# methods custom-attributes microsoft-test-manager


【解决方案1】:

我知道可以使用属性完成此操作的唯一方法是使用 post sharp 和方法 interception 之类的产品进行面向方面的编程。或者,如果方法是在接口中定义的,也可以使用 RealProxy 来完成,但这似乎有点过头了。

【讨论】:

  • 我一定会看看面向方面的编程。非常感谢
【解决方案2】:

你这样做的方式实际上非常好。但是正如Evk 在 cmets 中指出的那样:您应该将“跳过检查”提取到一个单独的方法中,特别是如果检查始终相同并且需要全局更改。使用属性可以解决问题,但使用起来有点复杂。

相反,请查看下面的代码。看起来很清楚,不是吗?不要使用太多的 cmets(并且不要将它们复制粘贴到每种方法中,那是没有用的)。这样,您可以获得与使用自定义属性相同的好处,但没有使用反射的丑陋。

public void AssertUser(UserDTO expectedUserInfo)
{
    VerifyUserName(expectedUserInfo.name);
    VerifyUserSurname(expectedUserInfo.surname);
    VerifyUserAge(expectedUserInfo.age);
    VerifyUserHeight(expectedUserInfo.height);
}

private void VerifyUserName(string name)
{
    if (ShouldSkipValidation(name)) return;
    // code here...
}

private void VerifyUserSurname(string surname)
{
    if (ShouldSkipValidation(surname)) return;
    // code here...
}

private void VerifyUserAge(string age)
{
    if (ShouldSkipValidation(age)) return;
    // code here...
}

private void VerifyUserHeight(string height)
{
    if (ShouldSkipValidation(height)) return;
    // code here...
}

// The MTM scenario does not want to validate values that satisfy the check below
private bool ShouldSkipValidation(string value)
{
    return string.IsNullOrWhiteSpace(value) || value == "<>";
}

【讨论】:

  • 谢谢你和@Evk的回答,作为最简单的回答,采纳
【解决方案3】:

我不认为属性可以实现您想要实现的目标。

但您可以改用自定义方法调用程序:

static void Main(string[] args)
{
    InvokeIfNotNullOrWhitespace((inputStr) => TestMethod(inputStr), null);
    InvokeIfNotNullOrWhitespace((inputStr) => TestMethod(inputStr), "");
    InvokeIfNotNullOrWhitespace((inputStr) => TestMethod(inputStr), "abc");

    // RESULT:
    // Trying to invoke action...
    // Trying to invoke action...
    // Trying to invoke action...
    // I have been invoked!
}

static void InvokeIfNotNullOrWhitespace(Action<string> action, string inputString)
{
    Console.WriteLine("Trying to invoke action...");
    if(!string.IsNullOrWhiteSpace(inputString))
        action.DynamicInvoke(inputString);
}

static void TestMethod(string input)
{
    Console.WriteLine("I have been invoked!");
}

我认为属性不起作用的原因是因为它们无法控制方法内部发生的事情。相反,“其他外部事物”可以查看这些属性并决定要做什么。

要实现您想要实现的目标,“外部事物”需要查看属性并决定它是否被执行。这相当于我写的:一个统一“检查字符串有效性”过程的外部调用程序。

【讨论】:

  • 这基本上是在调用另一个方法,它将检查参数的验证并决定是否调用最终方法!我想 OP 一定知道这种方法。
【解决方案4】:

这是我的 4 美分,

  1. 调用属性涉及反射,这已经是一个坏主意 您需要确定该属性是否已设置;
  2. 您正在避免代码中的 "1 liner" 实际上是 易于输入;
  3. 使用方法重载;
  4. 您可以使用Aspect oriented programming,它基本上会在编译时将以下示例注入您的代码中。您可以使用注释控制它的工作方式,并且不会对生成的运行时产生负面影响。

这里有一些变化:

//1
if(string.IsNullOrEmpty(testString))
   return;
//2
if(string.IsNullOrEmpty(testString) ||string.IsNullOrWhiteSpace(testString) )
   return;

要完成3,请确保不使用“缺少”文本的“返回NULL”返回NULL,或Boolean True / False。只有你知道你的代码应该如何流动。

也许您正在寻找方法重载 您可以通过在同一个类中创建 2 个具有相同名称的方法来做到这一点。 您可以从 MyMethod(with string) 中调用空的 MyMethod(),这样就不会重复逻辑。

return string.IsNullOrEmpty(testString)?MyMethod():MyMethod(testString);

【讨论】:

    猜你喜欢
    • 2011-01-19
    • 2011-07-11
    • 2018-04-20
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    • 2013-06-26
    相关资源
    最近更新 更多