1

Ok this is more curiosity than practical requirement.

Let's say I have this class:

public sealed class Entity
{
    int value;

    Entity()
    {

    }

    public static implicit operator Entity(int x)
    {
        return new Entity { value = x };
    }
}

I don't want this class to be instantiated outside the class and this works. But here Entitys will be referenced and not copied like value types. This class being so small I want it to behave likes value types. Also if I compare two instances of Entitys like e1 == e2 it's going to give reference equality (ok I can overload == but that's more work). So I would make it a struct:

public struct Entity
{
    int value;

    public static implicit operator Entity(int x)
    {
        return new Entity { value = x };
    }
}

But now someone can do new Entity() easily.

My question is there a way I can have a type (struct/class) that

1) will be copied every time the value is accessed, so that

Entity e1 = 1;
Entity e2 = e1;
ReferenceEquals(e1, e2); // prints false, just like any value type

2) has value semantics when comparing for equality

Entity e1 = 1;
Entity e2 = 1;
e1.Equals(e2); // prints true

// this is easy though by overriding default equality.

3) is not instantiable outside the scope of the type:

Entity e = new Entity(); // should not compile

Basically close to an enum's behaviour. I can certainly live without it, just learning.

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • Do you mean short of overriding the "Equals" method or the "==" operator on the class to perform the comparison as you want? – David W Feb 19 '13 at 15:02
  • @DavidW Yes, thats possible. But it still wouldnt give value type semantics for a class, or in other words my requirement 1. – nawfal Feb 19 '13 at 15:09

3 Answers3

2

Its not possible to do re-define or try to hide the default constructor for a struct in C# because the C# compiler routinely comples code based on the assumption that the parameterless constructor does nothing. See Why can't I define a default constructor for a struct in .NET?.

Your options are either

  • Use a class
  • Ensure that the default value for your struct has some meaning (e.g. in your case the int will be initialised to 0) - this is what enums do (recall that enums are initialised to 0).

My recommendation would be to use a class until you identify that there is a performance problem.

Community
  • 1
  • 1
Justin
  • 84,773
  • 49
  • 224
  • 367
1

A type has no say over what can happen to storage locations of that type. Storage locations of any type can come into existence, or have their contents overwritten with the content of other locations of the same type, without the type itself having any say in the matter. A storage location of a reference type holds a class reference; when it comes into existence, it will be a null reference. The only way a storage location of a reference type will hold anything other than a null reference is if an instance of that type is created and--outside some scenarios involving Full Trust Code and reflection--a reference type will have full control over the circumstances where that can occur.

By contrast, a storage location of a value type is an instance of that type. Consequently, there is no way a structure can control how one comes into existence. What a structure can control--to a limited extent--is what values its fields can contain. In limited trust scenarios, a private struct field can only assume a non-default value if the code for the struct writes that value to some instance of the struct (if the value gets written to any instance of the struct, even limited-trust code with access to that instance may be able to use multi-threaded overlapped reads and writes to produce a struct with a combination of field values which the struct itself would never create itself).

I would suggest creating a struct with a single private field of a class type. You cannot prevent code from creating a default(Entity), but that doesn't mean you have to allow code to do anything useful with one. Its Equals method should work well enough to return true when comparing to another default instance, and false when compared with anything else, and GetHashCode should return the same value for all default instances, but any other methods could throw an exception.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • What benefit is it giving me if I have private backing field of class type? a struct is still instantiable. I will go with my original approach (Justin's answer) sadly. – nawfal Feb 20 '13 at 06:52
  • @nawfal: Wrapping a class in a struct allows you to expose different methods from what the class itself would expose, and also allows you to control the behavior of default instances. Anyone can create a variable of any type--class or struct--and call a method passing that variable. In the case of a class type, the default-valued variable will compare equal to `null` and trying to invoke any method will throw `NullReferenceException`. With a struct, you can decide what effect methods should have on the default instance. – supercat Feb 20 '13 at 16:17
-1

Make all of the constructors (besides the static constructor) private for the class Entity. protected would work too, but since its sealed, that doesn't make a lot of sense.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445