I have populated an array of type ICanvasEffect with objects of derived classes, such as GaussianBlurEffect, PosterizeEffect, etc.
List<ICanvasEffect> effects = new List<ICanvasEffect>();
effects.Add(new GaussianBlurEffect
{
BlurAmount = 4.0f,
BorderMode = EffectBorderMode.Soft,
Optimization = EffectOptimization.Balanced
});
effects.Add(new PosterizeEffect
{
RedValueCount = 3,
GreenValueCount = 3,
BlueValueCount = 3
});
I now want to set the input sources of those effects in an abstract way. For example,
effects[0].Source = inputBitmap;
effects[1].Source = effects[0];
However, in order to call the Source property, I must provide knowledge of the specific class. For example,
(effects[0] as GaussianBlurEffect).Source = inputBitmap;
(effects[1] as PosterizeEffect).Source = effects[0];
How could this be done in an abstract way as in, for example, this fashion?
(effects[0] as effects[0].GetType()).Source = inputBitmap;
(effects[1] as effects[1].GetType()).Source = effects[0];
CodePudding user response:
That's a pretty good example for the Builder Design Pattern. It could looke like this:
using System.Drawing;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Bitmap inputBitmap = default;
var canvasBuilder = new CanvasBuilder()
.AddEffect(new CanvasEffect1())
.AddEffect(new CanvasEffect2());
Bitmap bitmap = canvasBuilder.Build(inputBitmap);
}
}
public interface ICanvasBuilder
{
ICanvasBuilder AddEffect(ICanvasEffect effect);
Bitmap Build(Bitmap input);
}
public class CanvasBuilder : ICanvasBuilder
{
private List<ICanvasEffect> _effects = new List<ICanvasEffect>();
public ICanvasBuilder AddEffect(ICanvasEffect effect)
{
_effects.Add(effect);
return this;
}
public Bitmap Build(Bitmap input)
{
foreach (var effect in _effects)
{
effect.ApplyEffect(input);
}
return input;
}
}
public interface ICanvasEffect
{
Bitmap ApplyEffect(Bitmap input);
}
public class CanvasEffect1 : ICanvasEffect
{
public Bitmap ApplyEffect(Bitmap input)
{
//modify bitmap
return input;
}
}
public class CanvasEffect2 : ICanvasEffect
{
public Bitmap ApplyEffect(Bitmap input)
{
//modify bitmap
return input;
}
}
dotnetfiddle: https://dotnetfiddle.net/W1UBlL
CodePudding user response:
It would have been smarter if the authors of GaussianBlurEffect
, PosterizeEffect
etc. had used some common interface which included an IGraphicsEffectSource Source { get; set; }
member. Then you could have utilized that interface.
However, that does not seem to be the case.
The syntax you propose:
(effects[0] as effects[0].GetType()).Source = inputBitmap;
(effects[1] as effects[1].GetType()).Source = effects[0];
is not legal, of course. The allowed thing corresponding to this, is:
((dynamic)effects[0]).Source = inputBitmap;
((dynamic)effects[1]).Source = effects[0];
When you use the dynamic
keyword, the binding of the member .Source
is postponed until run-time. So the C# compiler will produce CIL bytecode that will search for a member .Source
, given the run-time type (similar to the .GetType()
in your attempt), and if found, will attempt the assignment (which is going to correspond to calling a set
accessor).
dynamic
has a number of disadvantages, of course. Checks that are normally done when you compile your C#, are with dynamic
postponed to run-time, which can lead to fails or slower execution.