Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force a child class to initialize a variable

I have a class Foo that has a field _customObject that must be initialized. I also have a class Bar that inherits from Foo:

public abstract class Foo
{
    protected CustomObject _customObject;

    public Foo()
    {
        // Do stuff
    }
    // Other methods that use _customObject
}

public class Bar : Foo
{
    // Constructor and other methods
}

I can not initialize the object _customObject in Foo because every child inherited contains a different child of CustomObject, so it must be initialized in every child class:

public class Bar : Foo
{
    public Bar()
    {
        _customObject = new CustomObjectInherited1();
    }
}


public class Baz : Foo
{
    public Baz()
    {
        _customObject = new CustomObjectInherited2();
    }
}

Other people are going to implement new classes that inherit from Foo, so I was wondering if there is a way that an error in build time is shown, similar to when an abstract method is not implemented. If CustomObject is not initialized, a NullReferenceException will be thrown due to the use of the _customObject variable, ending in an application crash.

like image 805
Ignacio Avatar asked Mar 09 '16 16:03

Ignacio


3 Answers

You can add a parameter to your Foo constructor:

public abstract class Foo
{
    protected CustomObject _customObject;

    public Foo(CustomObject obj)
    {
        // Do stuff
        _customObject = obj;
    }
    // Other methods that use _customObject
}

Your derived classes will then be forced to call it, passing in a CustomObject, or something derived from it:

public class Bar : Foo
{
    public Bar():base(new CustomObjectInherited1())
    {

    }
}

Not calling the base constructor will result in a compile time error. This doesn't entirely protect you, as someone could still pass null to the base constructor, but at least they'll have an explanation as to why they're getting a NullReferenceError at runtime.

like image 59
James Thorpe Avatar answered Oct 13 '22 10:10

James Thorpe


You can force it by creating a abstract method which requires child classes to override it.

public abstract class Foo
{
    protected abstract CustomObject CreateCustomObject();
}

public class Bar : Foo
{
    protected override CustomObject CreateCustomObject()
    {
        return new BarCustomObject();
    }
} 

Or my favorite solution: Enforce it by generic constraints.

public abstract class Foo<T> : where T : CustomObject, new()
{
    protected T _customObject;
    public Foo()
    {
        this.CustomObject = new T();
    }
}

public class Bar : Foo<BarCustomObject>
{
}
like image 6
Jürgen Steinblock Avatar answered Oct 13 '22 10:10

Jürgen Steinblock


The answer provided by "James Thorpe" is correct (I've upvoted it already), but I wanted to share just another option here: You could mark your class as abstract and introduce an abstract property instead of the "_customObject" field. That way, at least the first initializer will be forced to implement it. The downside is that you'll loose the enforcement on subsequent level subclasses:

public abstract class Foo
{
    protected abstract CustomObject CustomObject {get; }

    public Foo()
    {
        // Do stuff
    }
    // Other methods that use _customObject
}

public class Bar : Foo
{
    // Constructor and other methods

    protected override CustomObject CustomObject
    {
        get { return "X"; }
    }
}

Also, with the first solution it's possible to validate the passed in value in the constructor - though, that'll be a runtime validation.

like image 1
Artak Avatar answered Oct 13 '22 09:10

Artak