5

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.

Marnix
  • 6,384
  • 4
  • 43
  • 78
  • This could only be done by the runtime, and even then not by passively inspecting things but by active tracking. Consider `new Car[100]` -- this just allocates a block of struct-sized memory to be carved up as an array. It technically also creates 100 instances, but this isn't counted as such anywhere. Meanwhile, calling a method that takes such a struct implicitly "creates" a copy, so add one more. Maintaining this count accurately and in real time per type would add so much overhead that it's not worth doing -- this is more in the realm of memory profilers. – Jeroen Mostert Jun 13 '19 at 11:12
  • @Rahul I changed the example to a `List.Add()`. – Marnix Jun 13 '19 at 11:16
  • var car1 = new Car(""); var car2 = new Car(""); Console.WriteLine(Car.ObjectsConstructed); then definitely you will get count as 2 – Manu Mohan Jun 13 '19 at 11:16
  • @ManuMohanT That is not the example I am looking for. I am looking for indirect copies that are made because of parameter passing or adding objects to lists. I have changed the example. – Marnix Jun 13 '19 at 11:17
  • 1
    `carList.Add(car1);` also does not create new instance. – Chetan Jun 13 '19 at 11:17
  • @ChetanRanpariya What?! Then, how does this work with structs? Can someone point me to a good reference that explains me how it works with value types? – Marnix Jun 13 '19 at 11:18
  • in such case you can just get the number of items in list using the `Count()` property of `List` – Rahul Jun 13 '19 at 11:18
  • @Marnix indirect copy?? there is no two objects in either of your examples. Reference doesnt mean that holds a fresh object. you already have the exact count of objects created – Manu Mohan Jun 13 '19 at 11:19
  • @Rahul Actually, `car2` was a different thing to `car1`, `struct` is a value type so an assignment like this would be a "new" object. – DavidG Jun 13 '19 at 11:25
  • I did some more tests with a unit test. `car1` and `car2` are definitely not the same. – Marnix Jun 13 '19 at 11:26
  • @DavidG Ahh!! that's a big mistake. didn't see that it's a `struct`. – Rahul Jun 13 '19 at 11:27
  • 1
    @Marnix The short answer is that you can't track them, in the same way you can't track `int` or `double`. `struct`s have an implicit parameterless constructor so even if you define your own, there's nothing preventing code calling the parameterless version. – DavidG Jun 13 '19 at 11:30
  • Basically, you can't do this. The compiler could even create copy operations behind the scenes which aren't always obvious. But it's rarely useful to know this anyway, in my experience - it would usually be a matter of avoiding copying for performance reasons, at which point you want decent benchmarking rather than a "number of copies made". – Jon Skeet Jun 13 '19 at 11:30
  • @JonSkeet I wanted to create an example for my students to show that it is indeed different for classes and structs and that you might not always want to use a struct. If it's even more than what you would theoretically expect, I'm still happy with any number that's bigger than 1. – Marnix Jun 13 '19 at 11:32
  • @DavidG I tried to make a parameterless constructor, but it's prohibited in structs. – Marnix Jun 13 '19 at 11:33
  • @Marnix No, I mean you cannot create one because it already exists. – DavidG Jun 13 '19 at 11:37
  • 4
    I'm afraid there just isn't any way of doing this. I'd also try to avoid referring to "struct objects" - they're not objects in the normal sense. They're values. – Jon Skeet Jun 13 '19 at 11:38
  • Perhaps your students aren't ready to understand the deep underlying reasons and should just accept your best practice rules? – NetMage Jun 13 '19 at 22:47

0 Answers0