帮我用out关键字清理这个疯狂的lambda

时间:2011-01-05 18:00:59

标签: c# lambda delegates refactoring

我的代码看起来很难看,而且我知道必须有更好的方法来做我正在做的事情:

private delegate string doStuff(
    PasswordEncrypter encrypter, RSAPublicKey publicKey,
    string privateKey, out string salt
);

private bool tryEncryptPassword(
    doStuff encryptPassword,
    out string errorMessage
)
{
    ...get some variables...
    string encryptedPassword = encryptPassword(encrypter, publicKey,
        privateKey, out salt);
    ...
}

到目前为止这个东西并没有打扰我。这就是我调用tryEncryptPassword看起来如此丑陋,并且有重复的原因,因为我用两种方法调用它:

public bool method1(out string errorMessage)
{
    string rawPassword = "foo";
    return tryEncryptPassword(
        (PasswordEncrypter encrypter, RSAPublicKey publicKey,
            string privateKey, out string salt) =>
            encrypter.EncryptPasswordAndDoStuff( // Overload 1
                rawPassword, publicKey, privateKey, out salt
            ),
        out errorMessage
    );
}

public bool method2(SecureString unencryptedPassword,
    out string errorMessage)
{
    return tryEncryptPassword(
       (PasswordEncrypter encrypter, RSAPublicKey publicKey,
           string privateKey, out string salt) =>
           encrypter.EncryptPasswordAndDoStuff( // Overload 2
               unencryptedPassword, publicKey, privateKey, out salt
           ),
       out errorMessage
   );
}

丑陋的两个部分:

  • 由于单个out参数,我必须明确列出lambda表达式中的所有参数类型。
  • EncryptPasswordAndDoStuff的两个重载除了第一个参数(可以是stringSecureString之外)采用所有相同的参数。因此method1method2几乎完全相同,只是调用EncryptPasswordAndDoStuff的不同重载。

有什么建议吗?

编辑(解决方案):我最终使用了Jeff的建议并更改了EncryptPasswordAndDoStuff的重载以返回EncryptionResult的实例。然后我不需要明确定义delegate,我使用了以下代码:

private bool tryEncryptPassword(KeysAndEncrypter keys,
    Func<EncryptionResult> encryptPassword,
    out string errorMessage
) { ... }

private class KeysAndEncrypter
{
    public RSAPublicKey PublicKey { get; set; }
    public string PrivateKey { get; set; }
    public PasswordEncrypter Encrypter { get; set; }
}

以下是method1的内容,其中method2非常相似:

string rawPassword = "foo";
KeysAndEncrypter keys = getEncryptionKeys();
return tryEncryptPassword(keys, () =>
    keys.Encrypter.EncryptPasswordAndDoStuff(
        rawPassword, keys.PublicKey, keys.PrivateKey
    ),
    out errorMessage
);

4 个答案:

答案 0 :(得分:5)

您可以引入一种新类型来表示委托的返回值:

 public class EncryptionResult {
     public string EncryptedValue { get; set; }
     public string Salt { get; set; }
 }

...并将委托更改为以下内容:

 private delegate EncryptionResult doStuff(
     PasswordEncrypter encrypter, 
     RSAPublicKey publicKey,
     string privateKey);

答案 1 :(得分:2)

为什么要打扰疯狂的lambda呢?

public bool Method1(out string errorMessage)
{
    return TryEncryptPassword("foo", out errorMessage);
}

public bool Method2(SecureString password, out string errorMessage)
{
    return TryEncryptPassword(password, out errorMessage);
}

private bool TryEncryptPassword(string password, out string errorMessage)
{
    GetSomeVariables();
    string encryptedPassword = encrypter.EncryptPasswordAndDoStuff(
           password, publicKey, privateKey, out salt);
    DoMoreStuff();
    // ...
}

private bool TryEncryptPassword(SecureString password, out string errorMessage)
{
    GetSomeVariables();
    string encryptedPassword = encrypter.EncryptPasswordAndDoStuff(
           password, publicKey, privateKey, out salt);
    DoMoreStuff();
    // ...
}

private void GetSomeVariables() { /* ...get some variables... */ }

private void DoMoreStuff() { /* ... */ }

答案 2 :(得分:1)

杰夫的建议是合理的。如果您不想在每次需要out参数时都定义新类型,也可以使用Tuple<string, string>

另一个建议是传入另一个委托来检索错误,而不是使用out参数。

private bool tryEncryptPassword(
     doStuff encryptPassword,
     Action<string> setError
)

public bool method1(Action<string> setError) {
    ...
    tryEncryptPassword(..., ..., ..., setError);
}

在调用method1之前,可以将setError分配给局部变量。

string error;
Action<string> setError = str => error = str;
method1(setError);        

答案 3 :(得分:0)

此外,如果您不想创建自定义类型,则可以在此处返回元组。

private delegate Tuple<string, string> doStuff(
   PasswordEncrypter encrypter, 
   RSAPublicKey publicKey,
   string privateKey);
在doStuff中,只需创建并返回一个元组:

Tuple<string, string> result = new Tuple<string, string>(EncryptedValue, Salt);
return result;