I am wrapping my head around functional programming in c# so I can lower the amount of side effects in my code as well as make testing easier and generalizing my code so refactoring can be easier. However, I have problems figuring out how to nest using
statements using a generalized using block. Consider the following:
public static class Disposable
{
public static TResult Using<TDisposable, TResult>
(
Func<TDisposable> factory,
Func<TDisposable, TResult> fn) where TDisposable : IDisposable
{
using (var disposable = factory())
{
return fn(disposable);
}
}
}
I invoke this code by using the following code sample:
Disposable.Using(
StreamFactory.GetStream,
stream => new byte[stream.Length].Tee(b => stream.Read(b, 0, (int)stream.Length)))
and I pass the output of this modified using
statement into another method in the pipeline.
I did however come across a caveat, where I am stuck.
What if I want to use nested using statements, but make modifications to items returned that I want to pass along?
Consider the following by reusing the Disposable
class I stubbed above:
Disposable.Using(
() => new PasswordDeriveBytes(PasswordConstants.CryptoKey, null),
password => Disposable.Using(
() => new RijndaelManaged(),
symmetricKey =>
Disposable.Using(
() => symmetricKey.CreateEncryptor(password.GetBytes(PasswordConstants.KeySize/8), Encoding.ASCII.GetBytes(PasswordConstants.Cipher)),
encryptor => encryptor)
));
This code works... however, what if I wanted to change symmetricKey's
encryption mode?
The following doesn't work:
Disposable.Using(
() => new PasswordDeriveBytes(PasswordConstants.CryptoKey, null),
password => Disposable.Using(
() => new RijndaelManaged(),
symmetricKey =>
{
symmetricKey.Mode = CipherMode.CBC; // ← this causes an issue, and also the fact that I made a **code block** here
Disposable.Using(
() => symmetricKey.CreateEncryptor(password.GetBytes(PasswordConstants.KeySize/8), Encoding.ASCII.GetBytes(PasswordConstants.Cipher)),
encryptor => encryptor);
}
));
What can I do to allow the modification of variables passed through the generalized Disposable.Using
method I created above?
I think you're just missing a return
. Try this.
Disposable.Using(
() => new PasswordDeriveBytes(PasswordConstants.CryptoKey, null),
password => Disposable.Using(
() => new RijndaelManaged(),
symmetricKey =>
{
symmetricKey.Mode = CipherMode.CBC;
return Disposable.Using(
() => symmetricKey.CreateEncryptor(password.GetBytes(PasswordConstants.KeySize/8), Encoding.ASCII.GetBytes(PasswordConstants.Cipher)),
encryptor => encryptor);
}));