Reputation: 1521
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?
Upvotes: 1
Views: 280
Reputation: 23149
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;
Upvotes: 3
Reputation: 17510
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 properties1. 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.
Upvotes: 4
Reputation: 1143
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.
Upvotes: 2