sillyrobot
sillyrobot

Reputation: 163

Accessing protected constructor from derived class instance

G'day Folks,

I'm struggling with what I thought was a simple issue, but I've gone blind. Obviously, I'm doing something basic very wrong, but I can't see it.

What I'm trying to do: Create an instance of a base class from a derived class instance using a protected constructor.

What's happening: The protected constructor of the base class is inaccessible inside the derived class. Message: 'BaseClass.BaseClass(List)' is inaccessible due to its protection level

Consider the following code:

class BaseClass
{
    protected readonly List<int> internalStuff;

    // Normal constructor for everywhere else
    public BaseClass()
    {
        internalStuff = new List<int>();
    }

    // this is the constructor I want to call; it shouldn't be accessible outside the derived classes hence protected
    protected BaseClass( List<int> stuff ) 
    {
        internalStuff = new List<int>(stuff);
    }
}

sealed class DerivedClass : BaseClass
{
    public void ConvertToBaseClass( out BaseClass newBaseClassInstance )
    {
        newBaseClassInstance = new BaseClass(internalStuff);
    }
}

The protected constructor should be visible to the derived class, right? Why is it inaccessible?

Edited to answer some questions. The code you see is the code that exists. I created the classes in a separate file and trimmed as much as sensible.

The reason I want to convert between the classes is the base class is "light" in that it contains no unmanaged code nor holds on resources. The derived class holds resources. When I am done with the derived class, but need to maintain the basic data, I want to convert to the base class.

Here is revised code to answer some of the comments below:

public class BaseClass
{
    protected readonly List<int> internalStuff;

    public BaseClass()
    {
        internalStuff = new List<int>();
    }

    protected BaseClass( List<int> stuff )
    {
        internalStuff = new List<int>(stuff);
    }
}

sealed class DerivedClass : BaseClass
{
    BaseClass bc;
    public void ConvertToBaseClass( out BaseClass newBaseClassInstance )
    {
        bc = new BaseClass(internalStuff); // <- error

        newBaseClassInstance = new BaseClass(internalStuff); // <- error
    }
}

Public on BaseClass did nothing, nor did I expect it to. Both BaseClass and DerivedClass are in the same assembly and namespace so are visible to each other anyway.

Even if there is a BaseClass variable internal to derived class, it gives the same inaccessible error.

DerivedClass is a derived class, go figure. Why does it not have access?

Upvotes: 0

Views: 1288

Answers (4)

Chris Schaller
Chris Schaller

Reputation: 16689

There are two flaws to your request:

  1. the instance of DerivedClass already IS and instance of the BaseClass so there is no need to convert it!
  2. Your DerivedClass has not implemented the constructor overload with stuff parameter so there is no way for this class to access that constructor.

If the constructor is protected, then it can only be accessed via constructors in inherting classes or the base class itself, Normally we would use syntax like this when implementing a class and we want the specific base constructor to be called, but we only want internal logic to access it:

public sealed class DerivedClass : BaseClass
{
    public DerivedClass ()
        : base()
    {
        // the base implementation has been executed.
        // now perform any other init logic you need. 
    }
    private DerivedClass (List<int> stuff)
        : base(stuff)
    {
        // the base implementation has been executed.
        // now perform any other init logic you need.
    }

    // this is one way to access non-public constructors
    public static DerivedClass CreateAnotherInstance(List<int> stuff)
    {
        return = new DerivedClass(stuff);
    }
}

What you may not be aware of, and the reason that you don't need a Convert method at all is that and instance of DerivedClass IS an instance of BaseClass, the following statement is valid:

DerivedClass car = new DerivedClass();
BaseClass vehicle = car;

The following will also work if you are unsure if the base object was originally a specific derived implementation:

if (vehicle is DerivedClass derivedRef)
{
    Console.Out.WriteLine("It was a DerivedClass instance all along");
}
else
{
    Console.Out.WriteLine("Not a DerivedClass: " + vehicle.GetType().Name);
}

If your DerivedClass instance convert method returned a new instance of BaseClass then you would have to copy across all the neccessary properties and would lose the polymorphic abilities that Inheritance provides for our classes.

In regard to your update:

The reason I want to convert between the classes is the base class is "light" in that it contains no unmanaged code nor holds on resources. The derived class holds resources. When I am done with the derived class, but need to maintain the basic data, I want to convert to the base class.

I really can't understand why you would, that's not true, I understand your reasoning, I just don't think it is a good design, even to the point that i feel very strongly against providing an answer that will help you continue down this path... this type of pattern really doesn't need to use inheritance at all... with extreme caution, as in please do not do this:

public class BaseClass
{
    protected readonly List<int> internalStuff;

    public BaseClass()
    {
        internalStuff = new List<int>();
    }

    protected BaseClass(List<int> stuff)
    {
        internalStuff = new List<int>(stuff);
    }

    protected BaseClass CreateAsBase()
    {
        return new BaseClass(internalStuff);
    }
    // I see little point in allowing "stuff" to be passed through
    // from the caller, this class already has access to the "stuff"
    // but you've already demonstrated an unual taste for hard work
    protected BaseClass CreateAsBase2(List<int> stuff)
    {
        return new BaseClass(stuff);
    }
}

sealed class DerivedClass : BaseClass
{
    public BaseClass ConvertToBaseClass()
    {
        return base.CreateAsBase();
        ...
        // um, if you really need to, this is how you could pass the "stuff"
        // I asusme you would only do this because you were going to modify
        // it in a way that the base class could not anticipate.
        return base.CreateAsBase2(aModifiedListOfInternalStuff);
    }

}

Usually in cases like this, either there would be a Utility class or methods to handle the processing and intensive resource requirements, and you would pass around a reference to the DTO (Data Transfer Object) so you can discard the wrapper object, but the DTO would remain intact:

public class Vehicle
{
    protected readonly List<int> internalStuff;

    public Vehicle()
    {
        internalStuff = new List<int>();
    }

    protected Vehicle(List<int> stuff)
    {
        internalStuff = new List<int>(stuff);
    }

    public Color Color { get; set; }
}

public sealed class Car : Vehicle
{
    public Car()
        : base()
    {
    }
    private Car(List<int> stuff)
        : base(stuff)
    {
    }

    public void AddOne()
    {
        internalStuff.Add(1);
    }
}

// Wrapper that accepts a vehicle, and does "stuff" to it
public sealed class VehicleBusinessObjectWrapper : IDisposable
{
    public Vehicle Vehicle { get; private set; }

    public VehicleBusinessObjectWrapper(Vehicle dto)
    {
        Vehicle = dto;
    }

    public void ChangeColour(Color newColor)
    {
        Vehicle.Color = newColor;
        /// you know, apply modifications to the Vehicle
    }

    public void Dispose()
    {
        // clean up your resources... if there are any
        // for instance, deliberately clear the reference to the original DTO, just to prove a point
        Vehicle = null;
        // after this point, the Vehicle Object is still intact.
    }
}

public static class BusinessLogic
{
    public static void Test()
    {
        var originalCar = new Car();
        using (var processor = new VehicleBusinessObjectWrapper(originalCar))
        {
            // We can still access the vehicle through the processor, we don't need inheritance
            if (processor.Vehicle.Color != Color.Green)
                processor.ChangeColour(Color.Green);

            // do other stuff on the car, or on the vehicle...
            if (processor.Vehicle is Car car)
            {
                car.AddOne();
            }
        }

        // reference to wrapper is now gone, but our original car/vehicle still remains.
        originalCar.ToString();
        Vehicle v = originalCar;

        Console.WriteLine(v == originalCar); // "True"

    }

}

Upvotes: 3

Jeremy Lakeman
Jeremy Lakeman

Reputation: 11163

... I am done with the derived class, but need to maintain the basic data ...

Well I wouldn't start from here... Instead of trying to solve this problem with inheritance, I would recommend solving it with composition.

Introduce a type to represent the structure of your basic data. Perhaps an interface to represent your basic functions (if any). Then your heavy resource classes can hold a reference to the basic data.

When you are done with the heavy class, you can keep a reference to the basic data and allow the heavy class to be garbage collected.

Upvotes: 0

CorrieJanse
CorrieJanse

Reputation: 2598

You can call the base class contractor from the derived class's constructor.

Implementation:

public class A
{
    protected List<int> MyList { get; }

    public A()
    {
        MyList = new List<int>();
        MyList.Add(1);
    }

    public int GetListCount()
    {
        return MyList.Count;
    }
}

public sealed class B : A
{
    public B() : base()
    {
    }

    public A GetBase()
    {
        return (A)this;
    }
}

Usage:

 public class Program
{
    public static void Main(string[] args)
    {
        B b = new B();

        var a = b.GetBase();
        Console.WriteLine(a.GetListCount());
    }
}

This returns 1

Upvotes: 0

Charlieface
Charlieface

Reputation: 72258

This is by design. protected members of the base class can only be accessed on a object reference of your own type. So new BaseClass(internalStuff) is invalid. But creating your own constructor that calls through to it would be valid:

DerivedClass(List<int> stuff) : base(stuff) {

Let's look at the specifications:

ECMA-335 specification, which defines the CLI on which C# is based, says as follows:

I.8.5.3.2 Accessibility of members and nested types

snip

  • family [this means protected in C#] – accessible to referents that support the same type (i.e., an exact type and all of the types that inherit from it). For verifiable code (see §I.8.8), there is an additional requirement that can require a runtime check: the reference shall be made through an item whose exact type supports the exact type of the referent. [my bold] That is, the item whose member is being accessed shall inherit from the type performing the access.

ECMA-334, which defines the C# language, says:

8.5.4 Protected access

When a protected instance member is accessed outside the program text of the class in which it is declared, and when a protected internal instance member is accessed outside the program text of the program in which it is declared, the access shall take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place through an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class. [my bold]

So this restriction stops you from creating an instance of the base class, or indeed another derived class altogether, and calling protected base functions on it.

The constructor is no exception. You must have an instance of DerivedClass to call a protected BaseClass constructor, and you can obviously only do that if you are inside the constructor of DerivedClass.

Upvotes: 1

Related Questions