使用自定义属性跳过正文方法

Use custom attribute to skip body method

我有以下功能:

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

在我的代码中的某些地方,我必须反复检查参数是否为空 string/null/whitespace 以跳过 body 方法。到目前为止,我常用的方法如下:

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 语句。因此,为什么我认为为每个验证信息的方法设置一个属性可能是个好主意。

P.S。我说的是数百种一开始就有 IF 实现的方法。

我认为属性无法实现您想要实现的目标。

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

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!");
}

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

要实现您想要实现的目标,"external thing" 需要查看属性并决定是否执行它。这相当于我写的:统一 "check string validity" 过程的外部调用程序。

据我所知,可以使用属性完成此操作的唯一方法是使用 post sharp 和方法 interception. Alternatively if the methods are defined in an interface this can also be done by using RealProxy 之类的产品进行面向方面的编程,但似乎有点矫枉过正。

这是我的 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 或基于 "missing" 文本的布尔值 true/false。只有您知道您的代码应该如何流动。

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

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

你的做法其实很不错。但正如 Evk 在评论中指出的那样:您应该将 "skip checking" 提取到一个单独的方法中,尤其是在检查始终相同且需要全局更改的情况下。使用属性可以解决问题,但使用起来有点复杂。

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

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 == "<>";
}