Помоги мне убрать эту безумную лямбду с помощью ключевого слова out - PullRequest
0 голосов
/ 05 января 2011

Мой код выглядит ужасно, и я знаю, что должен быть лучший способ сделать то, что я делаю:

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 параметра.
  • Две перегрузки EncryptPasswordAndDoStuff принимают все те же параметры, кроме первого параметра, который может быть либо string, либо SecureString. Так что method1 и method2 в значительной степени идентичны, они просто вызывают различные перегрузки EncryptPasswordAndDoStuff.

Есть предложения?

Редактировать (решение): В итоге я воспользовался предложением Джеффа и изменил перегрузки 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 ]

5 голосов
/ 05 января 2011

Вы можете ввести новый тип для представления возвращаемого значения делегата:

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

... и изменить делегат на что-то вроде этого:

 private delegate EncryptionResult doStuff(
     PasswordEncrypter encrypter, 
     RSAPublicKey publicKey,
     string privateKey);
2 голосов
/ 05 января 2011

Зачем вообще сумасшедшая лямбда?

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() { /* ... */ }
1 голос
/ 05 января 2011

предложение Джеффа разумно.Вы можете просто использовать Tuple<string, string>, хотя, если вы не хотите определять новый тип каждый раз, когда вам нужен выходной параметр.

Другим предложением будет передать другой делегат для получения ошибки, а неиспользуя параметр out.

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

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

Перед вызовом метода1 вы можете присвоить свой setError локальной переменной.

string error;
Action<string> setError = str => error = str;
method1(setError);        
0 голосов
/ 05 января 2011

Кроме того, если вы не хотите создавать пользовательский тип, вы можете вернуть здесь кортеж.

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