Home > Net >  How Can A Non-Abstract Property of a Predefined Abstract Class Be Accessed?
How Can A Non-Abstract Property of a Predefined Abstract Class Be Accessed?

Time:07-05

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.

  • Related