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

发布于 2024-10-10 11:48:13 字数 2552 浏览 3 评论 0原文

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

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

丑陋的两部分:

  • 我必须显式列出 lambda 表达式中的所有参数类型,因为单个 out 参数。
  • EncryptPasswordAndDoStuff 的两个重载采用除第一个参数之外的所有相同参数,该参数可以是 stringSecureString。因此,method1method2 几乎相同,它们只是调用 EncryptPasswordAndDoStuff 的不同重载。

有什么建议吗?

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

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
);

My code looks ugly, and I know there's got to be a better way of doing what I'm doing:

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);
    ...
}

This stuff so far doesn't bother me. It's how I'm calling tryEncryptPassword that looks so ugly, and has duplication because I call it from two methods:

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

Two parts to the ugliness:

  • I have to explicitly list all the parameter types in the lambda expression because of the single out parameter.
  • The two overloads of EncryptPasswordAndDoStuff take all the same parameters except for the first parameter, which can either be a string or a SecureString. So method1 and method2 are pretty much identical, they just call different overloads of EncryptPasswordAndDoStuff.

Any suggestions?

Edit (solution): I ended up using Jeff's suggestion and altering the overloads of EncryptPasswordAndDoStuff to return an instance of EncryptionResult. Then I didn't need an explicitly defined delegate, and I used the following code:

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

And here was the contents of method1, with method2 being very similar:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

洋洋洒洒 2024-10-17 11:48:13

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

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

...并将委托更改为如下所示:

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

You could introduce a new type to represent the delegate's return value:

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

... and change the delegate to something like this:

 private delegate EncryptionResult doStuff(
     PasswordEncrypter encrypter, 
     RSAPublicKey publicKey,
     string privateKey);
小女人ら 2024-10-17 11:48:13

为什么要为疯狂的 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() { /* ... */ }

Why bother with the crazy lambda at all?

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() { /* ... */ }
攒一口袋星星 2024-10-17 11:48:13

杰夫的建议是合理的。如果您不想每次需要输出参数时都定义一个新类型,您也可以使用 Tuple

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

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);        

Jeff's suggestion is reasonable. You could just use a Tuple<string, string> too though if you don't want to define a new type each time you need an out parameter.

Another suggestion would be to pass in another delegate to retrieve the error rather then using an out param.

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

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

Before calling method1, you can assign your setError to a local variable.

string error;
Action<string> setError = str => error = str;
method1(setError);        
妄想挽回 2024-10-17 11:48:13

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

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;

Also, if you don't want to create a custom type, you could return a tuple here.

private delegate Tuple<string, string> doStuff(
   PasswordEncrypter encrypter, 
   RSAPublicKey publicKey,
   string privateKey);

within doStuff, just create and return a tuple:

Tuple<string, string> result = new Tuple<string, string>(EncryptedValue, Salt);
return result;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文