I am looking for a way to determine the number of struct objects that are created in my program. It is for educational purposes.
I found this answer on SO, which works for classes: https://stackoverflow.com/a/12276687/363224. So I tried to do something similar with a struct, but as expected it doesn't work like that.
public struct Car
{
public string brand;
public static int ObjectsConstructed { get; private set; }
public Car(string brand)
{
this.brand = brand;
ObjectsConstructed++;
}
}
...
Car car1 = new Car("VW");
Car car2 = car1; // How can we increment the ObjectsConstructed?
List<Car> carList = new List<Car>();
carList.Add(car1); // How can we increment the ObjectsConstructed?
The Car(string) constructor is not called, because the copy of the struct object is called some kind of memcpy and doesn't go through the constructor. A struct also doesn't allow explicit parameterless constructors.
How does one make sort of a copy constructor that can be handled? Or is there another way to get this information out of the runtime via reflection?
EDIT
I wrote a test that shows what I mean:
// This test passes, firstCar and sameCar are not the same.
[TestMethod]
public void HowManyTimesIsACarCreated()
{
Car firstCar = new Car();
Car sameCar = firstCar;
sameCar.brand = "Opel";
// It seems that you can change sameCar without changing firstCar
Assert.AreNotEqual(firstCar.brand, sameCar.brand);
// This one is tricky, because firstCar and sameCar are passed as parameters, so new objects would again be created as I would see it.
Assert.IsFalse(ReferenceEquals(firstCar, sameCar));
}
firstCar and sameCar do not point to the same object, since I can change the brand if sameCar, but firstCar is still the same.
Related
I recently ran into the record keyword in c#, I'm able to create a an instance of a record and modify it both by assigning a value regularly and both by using the keyword with.
Is there any difference between the two ways, when should one use with?
public record Car{
public Car(string name, int age){
Name = name;
Age = age;
}
public string Name;
public int Age;
}
public static void Main()
{
var car = new Car("Reno", 15);
car.Name = "Honda";
Console.WriteLine(car.Name);
car = car with {Name = "BMW"};
Console.WriteLine(car.Name);
}
One of the main reasons for records introduction in C# - make it easier to create immutable data models. with functionality was created to provide easy to use syntax to create a copy of immutable instance with changed properties. So car = car with {Name = "BMW"}; actually does not modify original reference type but creates a new one and assigns it to the variable.
The difference can easily be seen with next code:
var car = new Car("Reno", 15);
var car2 = car;
car.Name = "Honda";
Console.WriteLine(car.Name);
Console.WriteLine(car2.Name);
car = car with {Name = "BMW"};
Console.WriteLine(car.Name);
Console.WriteLine(car2.Name);
Also couple of notes:
it is recommended to use autoproperties instead of fields, i.e.:
public record Car{
public Car(string name, int age){
Name = name;
Age = age;
}
public string Name { get; set; };
public int Age { get; set; };
}
in case you need immutable data records provide neat syntax which automatically generates constructor and init-only properties:
public record Car(string Name, int Age);
They are not the same. The difference is that with creates a copy of the record. According to the proposal:
A with expression allows for "non-destructive mutation", designed to produce a copy of the receiver expression with modifications in assignments in the member_initializer_list.
[...]
First, receiver's "clone" method (specified above) is invoked and its result is converted to the receiver's type. Then, each member_initializer is processed the same way as an assignment to a field or property access of the result of the conversion.
So there is an extra step of copying the record if you use with.
See also the decompiled C# code on SharpLab. Notice the <Clone> call before setting car2.Name = "BMW", which does not happen when setting car.Name = "Honda".
Car car = new Car("Reno", 15);
car.Name = "Honda";
Console.WriteLine(car.Name);
Car car2 = car.<Clone>$();
car2.Name = "BMW";
Console.WriteLine(car2.Name);
If I have a class, let's say Car.
class Car {
private Color m_Color;
private int m_Passengers;
}
Now, when I have this class at this state, I'm implementing this on X number of places in my software. Later on, my boss says he also want to store the max speed of the car. By all means, he is a nice guy and should have his property added.
class Car {
private Color m_Color;
private int m_Passengers;
private int m_MaxSpeed;
}
With this workflow, we might have something like this after a while
class Car {
private Color m_Color;
private int m_Passengers;
private int m_MaxSpeed;
private int m_Year;
private List<Seats> m_Seats;
private DateTime m_LatestCHeckup;
private double m_GasUsage;
}
Which is ok, but the 1st and 2nd version of the class is already implemented with fewer properties. How can I make sure, that if I add a property on a object that property must be used on all other objects initiated from the same class?
EDIT:
To clear it up.
I use this for my first car:
Car c = new Car();
c.m_Color = Color.White;
c.m_Passengers = 4;
and this for my 2nd car:
Car c2 = new Car();
c2.m_Color = Color.White;
c2.m_Passengers = 4;
c2.m_MaxSpeed=200;
and 3rd car
Car c3 = new Car();
c3.m_Color = Color.Green;
c3.m_Passengers = 8;
c3.m_MaxSpeed=180;
c3.m_Year = 2000;
c3.m_Seats = mySeatList;
c3.m_LatestCheckup = Datetime.Now;
c3.m_GasUsage=1.8;
I want to make sure that GasUsage is added on c and c2 when I've added it to the class. But c and c2 was created long time ago when GasUsage didnt exist. How do I prevent an object to not have all properties used? I might have one class, but 2000 objects.
The reason I want this behaviour is because I might loop through all the car objects and calculate for example how much gas they've used. Or similiar. If not all objects have a value in GasUsage that calc would fail.
If you have required properties that need to be initialised you should declare them in the constructor. Any optional values can be made into properties.
If you add any required properties to your Car class you should add them to the constructor, then the compilation will fail until previously-created values are fixed.
From your comment it looks like you want to add an interface with all the declared properties, then make them all constructor parameters in your implementing class:
public interface ICar {
Color Color { get; }
int Passengers { get; }
...
double GasUsage { get; }
}
class Car : ICar {
public Car(Color color, int passengers, int maxSpeed, ..., double gasUsage) {
this.m_Color = color;
this.m_Passengers = passengers;
...
this.m_GasUsage
}
private Color m_Color;
private int m_Passengers;
}
The initialization of the class instances could also be done with an constructor that demands all properties; all instatiations would then become syntactically incorrect as soon as a new property is added.
Assuming that 'Property must be used' means that you expect the instantiated objects to set a value for each of the new properties then you either change the constructor to require the new properties, set a default value for the new properties or raise an exception if a method on the object is called that requires a value to be set.
First version of the car class:
class Car
{
public Car(Color mColor, int mPassengers)
{
m_Color = mColor;
m_Passengers = mPassengers;
}
private Color m_Color;
private int m_Passengers;
}
Second version of the car class with constructor that has default value:
class Car
{
public Car(Color mColor, int mPassengers, int mMaxSpeed = 130)
{
m_Color = mColor;
m_Passengers = mPassengers;
m_MaxSpeed = mMaxSpeed;
}
private Color m_Color;
private int m_Passengers;
private int m_MaxSpeed;
}
I understand that these two objects point to the same reference, but I don't want that to be the case, so I am a bit confused on how to prevent one from changing the other. Do I need to declare a band new Car object, for example Car car2 = new Car();? When I do this resharper tells me it is unnecessary. Here is my code:
void Main()
{
Car car = new Car { Color = "Blue"};
Console.WriteLine(car.Color);
//Do I need Car car2 = new Car();
//car2 = car; //Confused.
Car car2 = car;
Console.WriteLine(car2.Color);
car = Format(car);
Console.WriteLine(car.Color);
Console.WriteLine(car2.Color); //How can I prevent car2 from changing color?
}
// Define other methods and classes here
public class Car
{
public string Color {get;set;}
}
public static Car Format(Car car)
{
car.Color = "Red";
return car;
}
var car2= new Car();
car2 = car;
car = Format(car); //This changes car and car2
When I do this resharper tells me it is unnecessar
Then Resharper would be wrong in this case. But it depends on your exact code.
When you want car1 and car2 to be different colors then they should be different instances. And that does require Car car2 = new Car();
Car is a class is a reference-type.
In your posted code there is only 1 instance, so there can only be one color.
Note that for the same reason your Format method does not need to return anything, the following will do exactly the same:
public static void Format(Car car)
{
car.Color = "Red";
}
As you mention, car2 and car refer to the same object. It sounds as if you don't want that, so you will need to create a copy of car and call it car2.
You could define a copy constructor for your Car class:
public Car(Car car)
{
this.Color = car.Color;
}
Then use it like this:
Car car2 = new Car(car);
You can make Car a struct
public struct Car
{
public string Color { get; set; }
}
or make Car ICloneable or any other of the dozen ways to copy the object so you can easily have another instance. We need to see the code that shows the warning so we could figure out the problem. I'm not seeing the warning.
In WPF you have a lot of classes where you pass a property for processing; e.g. ValueConverters, RuleValidations, etc. You cast the property to the correct type and use it.
But I often need some of the other properties of a specific object for things like calculations and comparing values.
The last view days I have been experimenting with ugly stuff, like passing the entire object as a parameter, just to get to the instance object.
There must be a way to get to the object from one of it's own properties?
How would I go about that?
For instance in this code:
public class Car
{
public string Brand { get; set; }
public string Color { get; set; }
}
class Program
{
public static void Main(string[] args)
{
Car car = new Car();
car.Brand = "Porsche";
car.Color = "Yellow";
Test(car.Brand);
}
public static void Test(object value)
{
var brand = (String)value;
// Get the Car object instance reference from the passed object?
var carInstance = ...
Console.WriteLine("This {0} has a {1} paint job",
brand, carInstance.Color);
}
}
Sorry, but its impossible. You can't get Car instance from String (its Brand name) value.
car.Brand is not a property (say, PropertyInfo) in your code, it's just a value of String.
Just imagine:
// Test(car.Brand);
String brandName = car.Brand; // <- Just a string, nothing special
Test(brandName);
Even when we use Reflection, we can't get Object instance, the only thing we can get is
class (Type):
PropertyInfo pi = typeof(Car).GetProperty("Brand");
...
Type tp = pi.ReflectedType; // <- Car; Type only, not instance
Instead, you can pass Car instance and get all properties you want:
public static void Main(string[] args)
{
Car car = new Car();
car.Brand = "Porsche";
car.Color = "Yellow";
Test(car);
}
public static void Test(Car value)
{
Console.WriteLine("This {0} has a {1} paint job", value.Brand, value.Color);
}
Is there a reason you don't want to pass the whole object? I think that since objects by default are passed by reference in c# it's not much worse than passing an int (a reference is kind of a number that represents a specific place in memory) so it shouldn't kill you performance-wise to pass a whole object (unless your object is something ridiculously massive and you only need 2 properties from it).
I was having trouble setting properties and realized I could only set the properties within a function or method. My question is why is this the case?
Here is my code that works:
public class SomeClass
{
Car car = new Car();
public Car JustAMethod()
{
Car car = new Car();
car.year = 2012;
return car;
}
}
Why doesn't this work:
public class SomeClass
{
Car car = new Car();
car.year = 2012;//I get an error here
}
The language specification (for the most part) forbids the execution of arbitrary statements at the class level. All that can be done is to specify default values for static or instance members of the class.
All code, generally speaking, must be executed within methods of a class.
As AntLaC mentioned, you can get around this by specifying the value using the object initialization syntax. Since objects can be defined at the class level (as "default values for static or instance members"), using syntax like the below will also work:
public class SomeClass
{
Car car = new Car() {
year = 2012;
};
}
Because that is the way the language specification requires it.
Imagine the mess that would happen if it were possible to write anything anywhere you liked:
We'd have ended up with PHP, and that was certainly not a design goal of C#.
If you want to set specific properties you can try this without being in a method
Car car = new Car()
{
year = 2012
};
The simple answer is that those are just the rules of the language. However, you have a few options:
Create a constructor so that Car takes a year:
public class SomeClass
{
Car car = new Car(2012);
}
Use object initializers:
public class SomeClass
{
Car car = new Car() { Year = 2012 };
}
Use a method:
public class SomeClass
{
Car car = InitializeCar(2012);
private Car InitializeCar(int year)
{
Car car = new Car();
car.Year = 2012;
return car;
}
}
You can't set the property in that way, it isn't allowed. You can do it in a constructor/ in a method/in a property. Or in this way:
public class SomeClass{
Car car = new Car(){ year = 2012 };
}
Or you can pass to the constructor your value:
public class Car{
public int year;
public Car(int year){
this.year = year;
}
}
And:
public class SomeClass{
Car car = new Car(2012);
}