Home > front end >  the best way to initialize fields of more than one class (C#)
the best way to initialize fields of more than one class (C#)

Time:01-28

what is the best way to initialize fields (a large number) of classes of this form:


class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public Arm Arm { get; set; }
    public Leg Leg{ get; set; }

    public Person(string name, string surname, Arm arm, Leg leg...)
    {
        this.Name = name;
        this.Surname = surname;
        this.Arm = arm;
        this.Leg = leg;
        .
        .
        .
    }
}

class Arm
{
    public Hand hand { get; set; }
    public string Position { get; set; }

    public Arm(Hand hand, string position)
    {
        this.hand = hand;
        this.Position = position;
    }
}
class Hand
{
    public Finger Finger { get; set; }
    public Hand(Finger finger)
    {
        this.Finger = finger;
    }
}
class Finger
{
    // Something...
}

The way I used:

class Main
{
    Person Person1 = new Person("John", "Wick", new Arm(new Hand(new Finger()), "rightarm"));
}

This does not seem right to me, what if each constructor would have 4 parameters, the main class would be a mess. What would be the best and most appropriate way !?

CodePudding user response:

What you have here are generally regarded as "Entity" classes. The objects they model represent something (eg. a Book, a Person, a User), and it is quite usual to take constructor arguments which represent the properties.

Having a small number of arguments (say 1-5) is quite normal, as that number grows you might want to consider if you're modelling obejcts correctly. In your example you've done that right by breaking each object down into parts - refered to as composition - imagine if Person took arguments for name, legs x2, fingers x10, arms x2, legs x2, feet x2, toes x10.... bleugh!

As you get further away from these "Entity" classes, you'll find the problem goes away. Objects will take very few arguments or, more likely, you've used Dependency Injection and are no longer constructing your objects manually.


Sometimes complex objects can be created using a Builder - does this code look better to your eyes?

var builder = new PersonBuilder("John","Wick");
builder.AddArm(Arms.Left, armBuilder => 
           armBuilder.AddHand(handBuilder => 
               handBuilder.AddFinger("Thumb").AddFinger("Ring")))
       .AddLeg(Legs.Left, legBuilder =>
           legBuilder.AddFoot(footBuilder =>
               footBuilder.AddToe("Big"))
var person = builder.Build();

This is an example of a "Fluent Builder". It has advantages and disadvantages over your code.

CodePudding user response:

One option is to add default constructors (i.e. public Person(){}, public Arm(){}...) and use property initializers:

Person Person1 = new Person()
{
    Name = "John", 
    Surname = "Wick", 
    Arm = new Arm
    {
        Hand = new Hand
        {
            Finger = new Finger()
        }, 
        Position = "rightarm"
    }, 
    Leg = new Leg()
};

Downside of such approach is that complier will not enforce initialization of required members (Though based on the public setters - anybody can set nulls whenever is needed. Note that "readonly" scenarios are supported also for property initializers via init only setters). You can tackle this problem with custom analyzer (which can be a huge overkill though). I've played with something similar for records - here I've used source generator to add default constructor and implemented some basic analyzer that emitted errors whenever required (no default value) value from ctor was not initialized via property initialization syntax (but unfortunately hadn't time to finish).

  •  Tags:  
  • Related