简体   繁体   中英

How do I write a general method to copy instances of classes that all inherit from the same abstract base class?

I want to copy the details of one instance of a brush to a new instance as per the code below:

private static Brush CopyBrush(Brush b)
        {
            Brush b1 = new Brush();

            // code to copy settings from b to b1

            return b1;
        }

My difficulty is that Brush is an abstract class so I cannot instantiate it. I have a number of different classes inheriting from Brush and I could pass anyone of them to this method. The inherriting classes all use the same properties in Brush so copying them is not a problem. The return can be cast back to the inheriting type at the call source.

If all of your derived classes are guaranteed to have a constructor with a certain signature, eg without any arguments, you can instantiate the respective subtype based on its Type instance:

Brush b1 = (Brush)Activator.CreateInstance(b.GetType());

If that cannot be guaranteed, you'll need to introduce a virtual method in your Brush class to create a new instance of the current class. Subclasses can override it to invoke a suitable constructor.

More concretely, your Brush class could have the following method:

protected virtual Brush CreateBrushInstance()
{
    return (Brush)Activator.CreateInstance(GetType());
}

In any derived class that requires different constructor arguments, override this method and call the Activator.Create(Type, object[]) method with the appropriate argument values.

(This assumes that CopyBrush is a method of the Brush class. Otherwise, use internal or public visibility for the CreateBrushInstance method.)

Case 1: All your Brush -derived classes have a public default constructor, and all properties have public setters.

In this case, one generic method should suffice:

static TBrush Copy<TBrush>(this TBrush brush) where TBrush : Brush, new()
{
    return new TBrush() // <- possible due to the `new()` constraint ^^^
           {
               TypeOfHair = brush.TypeOfHair,
               NumberOfHairs = brush.NumberOfHairs,
               …
           };
}

Case 2: One or more of the above preconditions are not met; ie there is no public default constructor, or at least one property is not publicly settable.

You can use this pattern:

abstract class Brush
{
    protected abstract Brush PrepareCopy();
    // replacement for public default ctor; prepares a copy of the derived brush
    // where all private members have been copied into the new, returned instance.

    public Brush Copy()
    {
        // (1) let the derived class prepare a copy of itself with all inaccessible
        //     members copied:
        Brush copy = PrepareCopy();

        // (2) let's copy the remaining publicly settable properties:
        copy.TypeOfHair = this.TypeOfHair;
        copy.NumberOfHairs = this.NumberOfHairs;

        return copy;
    } 
}

sealed class FooBrush : Brush
{
    public FooBrush(int somethingPrivate)
    {
        this.somethingPrivate = somethingPrivate;
        // might initialise other members here…
    }

    private readonly int somethingPrivate;

    protected override Brush PrepareCopy()
    {
        return new FooBrush(somethingPrivate);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM