简体   繁体   中英

Class constructor in C#

I am somewhat of a C# noob, and have been looking around in documentation but have been unable to understand how to create a constructor method for my class as I would in python.

Say I have a class named Vehicle, and when I create an instance of vehicle, I need to input the number of wheels and the number of axles the vehicles has. I want the constructor to divide the total wheels by the total axles to set the property wheelsPerAxle:

public class Vehicle
{
    public int numWheels;
    public int numAxles;
    public int wheelsPerAxle;

    public Vehicle()
    {
        wheelsPerAxle = Mathf.CeilToInt(numWheels/numAxles);
    }

}

My code example is a little ridiculous but hopefully you get the idea that I want to call the method when the object is instantiated. How can I do this?

Let's go over first what actually happens with your code, so we can get an idea of how it works.

We'll use this code to make an instance of the class:

var vehicle = new Vehicle();

// set the items inside
vehicle.numWheels = 4;
vehicle.numAxles = 2;

Well, what does this do?

When code execution runs, it starts by running your constructor, and all the code inside it , before running the last two lines to modify the properties 1 . Your object must be fully instantiated before you can modify it.

Problem is, you'll never be able to fully instantiate the object.

Your class has two variables which you read from in the constructor, numWheels and numAxles . Both of these are (32 bit) integeters, and are uninstantiated - meaning you never directly assign them a value before you read from them.

However, int types are Value Types in c#, which means they are never uninstantiated . If you don't set a value when you create the variable it will take the default value for that type, which in this case is 0 .

And then you try to do 0/0 , and an exception will be thrown.


The issue here is that you need to provide a value for your variables during creation of the class , as doing so afterwards is too late. The good news is that you can pass in values as arguments to the class's constructor.

public class Vehicle
{
    public int wheelsPerAxle;

    public Vehicle(int numWheels, int numAxels)
    {
        wheelsPerAxle = Mathf.CeilToInt(numWheels/numAxles);
    }
}
var vehicle = new Vehicle(4, 2);
// "vehicle.wheelsPerAxel" is 2

Note: this doesn't do any validation, and it doesn't save (scope-wise) the values for use outside of the constructor, but this should get the point across.

See Pac0's answer for a more robust solution to modifying your class.


1 These are not properties, they are fields .

You can pass the parameters you need in the constructor.

Also, it's usually more safe to mark those fields initilized during construction as readonly if you know they can't change for a specific instance. (And in this case they must be assigned a value in the constructor.

public class Vehicle
{
    public readonly int numWheels;
    public readonly int numAxles;
    public readonly int wheelsPerAxle;

    public Vehicle(int numWheels, int numAxles)
    {
        // let's do some validation here
        if (numWheels < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(numWheels), "number of wheels must be non negative");
        }
        // And do something similar for number of axles

        //set the readonly fields, if you need them for another thing later
        this.numWheels = numWheels; // We must use "this" to refer to the fields to disambiguate between the field and the parameter because I chose to give them the same name. 
        this.numAxles = numAxles;

        // I'll let you handle the case where "numAxles" is zero. Maybe you allow it ? (in this case you can use a conditional operator), or throw an exception during validation if you don't allow it.
        wheelsPerAxle = Mathf.CeilToInt(numWheels/numAxles);
    }

}

Then you can instantiate like this, calling the constructor with the proper arguments as any method:

var car = new Vehicle(numWheels: 4, numAxles: 2); // Argument names are not mandatory, but good to avoid confusion while coding, since they are both int, you might invert them without noticing)

Also, here I used fields, as you did, but you can set properties as well. And auto-properties if you want. To make them readonly, just give them a get; , instead of get; set; get; set;

You mentioned Property, but what you have there are fields. Please read this answer to understand the difference. So I assumed that you really need property in my code:

The first solution is assuming that WheelsPerAxle is readonly property.

public class Vehicle
{
    public int NumWheels { get; set; }
    public int NumAxles { get; set; }

    //if you want to have it as readonly
   public int WheelsPerAxle => Mathf.CeilToInt(NumWheels / NumAxles);

    public Vehicle(int numWheels, int numAxles)
    {
        NumWheels = numWheels;
        NumAxles = numAxles;
    }

}

and second solution is assuming WheelsPerAxle has a setter

public class Vehicle
{
    public int NumWheels { get; set; }
    public int NumAxles { get; set; }

    //if you want to set it later on and change its value
    public int WheelsPerAxle { get; set; }

    public Vehicle(int numWheels, int numAxles)
    {
        NumWheels = numWheels;
        NumAxles = numAxles;
        WheelsPerAxle = Mathf.CeilToInt(NumWheels / NumAxles);
    }

}

Make sure to add some validations.

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