Baraa Baraa - 2 months ago 9
C# Question

Check if object is defined after initialization in c#

I have the following object (class).

namespace Temp.Models
{
public class CurrentClass
{
private double _firstCoefficient;
private double _secondCoefficient;

public double FirstCoefficient
{
get { return _firstCoefficient; }
set { _firstCoefficient= value; }
}
public double SecondCoefficient
{
get { return _secondCoefficient; }
set { _secondCoefficient= value; }
}
}
}


The following class utilizes the above object and therefore initializes the object as follows:

namespace Temp.Models
{
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();

public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
}
}


At some point if certain conditions are met I would define the variables as follows:

MainClass currentObject = new MainClass();
//if conditions are met
currentObject.CurrentClass.FirstCoefficient = 0;
currentObject.CurrentClass.SecondCoefficient = 5;


But what if the conditions are never met and I never define the above variables. How and/or what is the best way to check if the object was never defined?

I can do the following check:

if(currentObject.CurrentClass.FirstCoefficient != 0 && currentObject.CurrentClass.SecondCoefficent != 0)


But the values can be defined as 0...So I am not sure how to go about this.

Any help is much appreciated!

Answer

These are some principles that can be used for solving the problem with description, samples and brief evaluation/opinion.

1. Parametrization through constructors

According to OOP principles, a constructor is method used to initialize an object to a valid state. The concept of immutability takes this even further, disallowing any changes, completely avoiding invalid state.

There is also a possibility of compromise where the API of an object disallows invalid states.

With this concept, you would arrive to:

namespace Temp.Models
{
    public class CurrentClass
    {
        public double FirstCoefficient { get; private set; }
        public double SecondCoefficient { get; private set; }

        public CurrentClass(double firstCoefficient, double secondCoefficient)
        {
            FirstCoefficient = firstCoefficient;
            SecondCoefficient = secondCoefficient;
        }

        // if mutability is required - this is needless as the constructor is
        // the same but if there was more complex state, methods like this would make
        // sense, mutating only parts of the state
        public void SetCoefficients(double firstCoefficient, double secondCoefficient)
        {
            FirstCoefficient = firstCoefficient;
            SecondCoefficient = secondCoefficient;
        }
    }
}

Summary:

  • Each instantiation of CurrentClass is always in a valid state, avoiding a lot of consistency checks (improved encapsulation)

  • It takes more code to write (but you save a lot of other code due to the previous point)

  • You need to know the coefficients beforehand.

2. Using nullable types

Nullable types add the "additional" value to types, the "undefined" state. Reference types (class) are nullable by design while value types (struct) need to be marked nullable, either as Nullable<T> or with the shorthand T?.

This then allows the objects be in invalid state and be specific about it. This goes to the other end of consistency scale from immutability as an object with multiple nullable fields has many invalid states.

Sample code:

namespace Temp.Models
{
    public class CurrentClass
    {
        public double? FirstCoefficient { get; set; }
        public double? SecondCoefficient { get; set; }
    }
}

Now this gets instantiated quite nicely and can be changed on the fly:

public CurrentClass CreateCurrentClass()
{
    var currentClass = new CurrentClass { FirstCoefficient = 1.0 };
    var secondCoefficient = RetrieveSecondCoefficient();
    currentClass.SecondCoefficient = secondCoefficient;

    return currentClass;
}

You'll however need validity checks everywhere the object is used.

public bool IsValid(CurrentClass currentClass)
{
    // what if FirstCoefficient has value and SecondCoefficient doesn't,
    // is that always an invalid state?
    return currentClass.FirstCoefficient.HasValue
        && currentClass.SecondCoefficient.HasValue;
}

Summary:

  • Very little code is needed to have a DTO up and running

  • A lot of consistency checks (and related brain pain) are required to work with such model

  • Encapsulation is lacking - any method taking CurrentClass can alter its validity, therefore making the previous point even worse. This can be eased by usage of read-only interface passed where read-only access is required.

Summing up

There are many other means that usually lay in between the two aforementioned approaches. For example you can use one validity flag (SergeyS's response) per object and ease on the external validity checks but having more code in the class and the need of deeper thinking.

Personally, I prefer immutability. It's more monkey code to write but will definitely pay off down the road thanks to the clean design.

A complex system without immutability is very hard to reason about without extensive knowledge. This is especially painful when working in a team - usually each person only knows a part of the codebase.

The sad thing is that it's not always possible to have evertything immutable (e.g. viewmodels): then I tend to convert objects to an internal immutable model as soon as it's possible.