Is it possible to hide the parameterless constructor from a user in C#?
I want to force them to always use the constructor with parameters
e.g. this Position struct
public struct Position
{
private readonly int _xposn;
private readonly int _yposn;
public int Xposn
{
get { return _xposn; }
}
public int Yposn
{
get { return _yposn; }
}
public Position(int xposn, int yposn)
{
_xposn = xposn;
_yposn = yposn;
}
}
I only want users to be able to new up a Position by specifying the x and y coordinates.
However, the parameterless constructor is ALWAYS available.
I cannot make it private. Or even define it as public.
I have read this:
Why can't I define a default constructor for a struct in .NET?
but it doesn't really help.
If this is not possible - what is the best way to detect if the Position I am being passed has values?
Explicitly checking each property field? Is there a slicker way?
No, you can't do this. As you said, similar question has been asked before - and I thought the answer was fairly clear that you couldn't do it.
You can create a private parameterless constructor for a struct, but not in C#. However, even if you do that it doesn't really help - because you can easily work around it:
MyStruct[] tmp = new MyStruct[1];
MyStruct gotcha = tmp[0];
That will be the default value of MyStruct - the "all zeroes" value - without ever calling a constructor.
You could easily add a Validate method to your struct and call that each time you received one as a parameter, admittedly.
Nope can't hide it. Structs cannot redefine zero arg constructor, so therefore its visibility can't be redefined.
Well a struct is literally a declaration of how the memory will sit.
Even when you use an object, the objects pointer IS declared, regardless of whether it's null.
The reason that you can't hide the constructor is because the struct demands that the CLR can create it as internally it must do this.
You could convert this struct into an object to achieve this task. Or use static analysis to ensure it's intialized before you use it?
struct point
{
int xpos;
int ypos;
}
Have a google for immutable objects, this appears to be what your after. I believe that they are looking to add this feature (but not in C# 4) to the language itself, because it is a common requirement. Is there a specific need for a struct here?
You cannot make a struct with a private parameter-less constructor or even declare a parameter-less constructor. You would have to change it to a class. Structs are not allow to declare a parameterless constructor.
From the Structs Tutorial on MSDN:
Structs can declare constructors, but they must take parameters. It is an error to declare a default (parameterless) constructor for a struct. Struct members cannot have initializers. A default constructor is always provided to initialize the struct members to their default values.
From the C# specification on MSDN:
11.3 Class and struct differences
Structs differ from classes in several important ways:
Structs are value types (Section
11.3.1).
All struct types implicitly inherit
from the class System.ValueType
(Section 11.3.2). Assignment to a
variable of a struct type creates a
copy of the value being assigned
(Section 11.3.3).
The default value of a struct is the
value produced by setting all value
type fields to their default value
and all reference type fields to null
(Section 11.3.4). Boxing and
unboxing operations are used to
convert between a struct type and
object (Section 11.3.5).
The meaning of this is different for
structs (Section 11.3.6).
Instance field declarations for a
struct are not permitted to include
variable initializers (Section
11.3.7).
A struct is not permitted to declare
a parameterless instance constructor
(Section 11.3.8).
A struct is not permitted to declare
a destructor (Section 11.3.9).
Related
struct Mutable {
private int x;
public Mutable() { } //Compiler Error CS0171
}
So if i declare a constructor for a struct, It says that all the fields must be assigned but I am thinking why cant't the inline initializer just DEFAULT the field' values to their defaults?
I mean when no Constructor is specified the inline initializer does default the fields' values to their defaults.
Actually it can, but it is planned for the next language version - C# 11. From the docs:
Auto-default struct
The C# 11 compiler ensures that all fields of a struct type are initialized to their default value as part of executing a constructor. This change means any field or auto property not initialized by a constructor is automatically initialized by the compiler. Structs where the constructor doesn't definitely assign all fields now compile, and any fields not explicitly initialized are set to their default value. You can read more about how this change affects struct initialization in the article on structs.
Read more:
Proposal
MyClass[] CLASS = new MyClass[5];
int[] STRUCT = new int[5];
What exactly is new [] doing for a class vs a struct. Apparently the struct has some overloaded static index that causes it to run the default constructor of the struct. However the new [] for a class appears to do nothing but make space to initialize an instance of a class. How do I overload the static behavior of the class to also run the default constructor. I know how to use for loops and other methods of accomplishing this. My question is very specific to what is going on underneath the new []. I understand that a struct needs a default value. But doesn't a non-nullable class also need a default value which is why it gives an error when you try to use it? Or is this telling me that all classes are actually nullable?
No, the default constructor of struct isn't run. What happens instead is that in the struct case, the memory is initialized to zero. What this means depends on the data type. E.g., a reference field (like a class or a string) becomes null, numeric fields become 0, boolean fields become false, etc.
This differs very much from how classes work. This is why with the initialization of the class array, you see null values. Basically, it comes down to this. An "empty" class variable (or array in your case) becomes null. However, when you have an "empty" struct, you already have something valid. However, it is initialized as empty.
The easiest way to see this is when you e.g. have an int field in a class. This works basically the same way. When you add an int field to a class, you are not required to initialize it. When you don't initialize it, it gets the value 0 by default. struct's work the same way in this regard.
See http://msdn.microsoft.com/en-us/library/aa664471.aspx for a bit more information.
A few more things to note (answer to the question in the comments):
Classes are always nullable. This means that the only way to initialize the array is to create a loop and initialize a new instance for every item in the array;
The default constructor doesn't apply to structs, because they aren't allowed to have a default constructor. If you try to compile the following code snippet, you will get a compilation error telling you this:
struct MyStruct
{
public MyStruct()
{
}
}
From Pro C#
Referring to "New-ing" Intrinsic data types...
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
[...] Object references (including strings) are set to null.
In C#, strings do not have a public default constructor. My guess is that due to string's immutability, they have a private default constructor. But, the context here is talking about Object references and string's as a whole while using new.
Because one cannot do
String myString = new String();
So,
String a;
referencing string doesn't result in a "default value". Instead, it's a compiler error to access a.
Though
public class StringContainer
{
public static string myString { get; set; }
}
Results in a legally accessible string (defaulted to null). This doesn't use new. It performs some kind of magical construction.
What is occurring in the StringContainer scenerio? Because there appears to be no new-able default constructor in string, is this an error in the C# book?
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
There are an impressive number of subtle errors in that statement.
First off, there is no such thing as an "intrinsic" data type; perhaps this term is defined somewhere else in the book?
Second, it would be more accurate to say that all struct types have a public parameterless constructor called the "default" constructor. Some class types also have a public parameterless ctor; if you do not provide any ctor then the C# compiler will automatically generate a public parameterless ctor for you. If you do provide a ctor then the C# compiler will not do this for you.
Third, constructors do not create variables. The author is conflating a bunch of related but different things: the "new" operator, the memory manager, the constructor and the variable, and the created object. Variables are storage locations and are managed by the CLR; they are not created by the "new" operator.
The correct statement is that the "new" operator on a struct causes a variable to be created by the CLR on the temporary storage pool; that variable is then initialized by the memory manager, and then passed to the constructor for more initialization. The value thus created is then copied somewhere else. The "new" operator on a class causes the CLR to create an object on the long-term storage pool, and then pass a reference to that object to the CLR. No "variable" need be involved.
Confusing variables with objects is a very common error; it is valuable to understand the difference.
In C#, strings do not have a public default constructor.
Correct.
My guess is that due to string's immutability, they have a private default constructor.
Good guess, but wrong. There is no private parameterless constructor on string.
[an auto-property or field of type string] results in a legally accessible string (defaulted to null). This doesn't use new. It performs some kind of magical construction.
It does no such thing. A null reference is not a constructed object at all. It's the absence of a constructed object!
You're basically saying that my empty garage contains a "magically constructed" non-existing car. That is an exceedingly weird way to look at an empty garage; an empty garage contains no car at all, not a magically constructed non-existing car.
What is occurring in the StringContainer scenerio?
The containing type contains a compiler-generated field -- a variable -- of type string. Let's suppose the containing type is a struct or class. When the storage for the struct or class is initialized by the memory manager, the memory manager writes a null reference into the storage location associated with the variable.
Finally: I suspect your confusion is because you've gotten the "default constructor" and the "default value of a type" confused. For a struct, they are the same thing:
int x = new int();
and
int x = default(int);
both make an int initialized to zero.
For a class, they do not do the same thing:
Fruit f = new Fruit();
makes a new fruit reference and assigns the reference to variable f, whereas:
Fruit f = default(Fruit);
is the same as
Fruit f = null;
No constructor is called.
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
I'm not sure what the author means by "intrinsic data types". My best guess is that he actually means "value types" (i.e. types declared with C#'s struct keyword), because value types always have a default constructor, and reference types may not.
So if you have a field whose type is a struct type (e.g. Int32, CancellationToken), then that field will be initialized as if the type's default constructor was called.
In actual implementation, there's probably not an actual call to the type's default constructor -- the memory is just initialized to all zeroes, which is the same thing that would happen if you did call the default constructor. (That's why you can't provide your own parameterless constructor for a value type -- the parameterless constructor always initializes the memory to all zeroes. This greatly simplifies things like new int[10000] -- the compiler doesn't actually have to call new Int32() 10,000 times; it just zeroes out the memory.)
Your question about a string field in a class isn't really related to the author's discussion of "intrinsic data types", because both string and your enclosing class are reference types, not value types. So your class won't have a parameterless-constructor-that-you-can't-override; it will just have normal constructors. But the zeroing-out behavior is still there: when you call a constructor, the new memory block is zeroed out before the constructor code starts to run. Your string field is a reference type, and a zero reference is null.
I imagine it is using default(string) which returns null because string is a reference type.
Also, remember that constructors can't return null.
Consider this code block:
struct Animal
{
public string name = ""; // Error
public static int weight = 20; // OK
// initialize the non-static field here
public void FuncToInitializeName()
{
name = ""; // Now correct
}
}
Why can we initialize a static field inside a struct but not a non-static field?
Why do we have to initialize non-static in methods bodies?
Have a look at Why Can't Value Types have Default Constructors?
The CLI expects to be able to allocate and create new instances of any value type that would require 'n' bytes of memory, by simply allocating 'n' bytes and filling them with zero. There's no reason the CLI "couldn't" provide a means of specifying either that before any entity containing structs is made available to outside code, a constructor must be run on every struct therein, or that a whenever an instance of a particular n-byte struct is created, the compiler should copy a 'template instance'. As it is, however, the CLI doesn't allow such a thing. Consequently, there's no reason for a compiler to pretend it has a means of assuring that structs will be initialized to anything other than the memory-filled-with-zeroes default.
You cannot write a custom default constructor in a structure. The instance field initializers will eventually need to get moved to the constructor which you can't define.
Static field initializers are moved to a static constructor. You can write a custom static constructor in a struct.
You can do exactly what you're trying. All you're missing is a custom constructor that calls the default constructor:
struct Animal
{
public string name = "";
public static int weight = 20;
public Animal(bool someArg) : this() { }
}
The constructor has to take at least one parameter, and then it has to forward to this() to get the members initialised.
The reason this works is that the compiler now has a way to discover the times when the code should run to initialise the name field: whenever you write new Animal(someBool).
With any struct you can say new Animal(), but "blank" animals can be created implicitly in many circumstances in the workings of the CLR, and there isn't a way to ensure custom code gets run every time that happens.
I would like to try this code:
public struct Direction
{
private int _azimuth;
public int Azimuth
{
get { return _azimuth; }
set { _azimuth = value; }
}
public Direction(int azimuth)
{
Azimuth = azimuth
}
}
But it fails on compilation, I understand that struct need to init all its fields.
but i am trying to understand what happens under the CLR\IL hoods.
why it need all the fields before any other method\property\this etc.
Thanks.
Value Types are created on the stack (unless nested within a reference type) There is something about fields/locations on the stack that the CLR can't guarantee that they will be zeroed out (contrary to fields/locations on the managed heap which are guaranteed to be zeroed out). Hence they must be written to before being read. Otherwise it's a security hole.
The struct's default ctor (which takes no parameters and which you're not allowed to explicitly specify) zeroes out all fields of a struct and hence you can use a struct after you do.
new BimonthlyPairStruct()
However when you implement your parameterized ctor, you must ensure that all fields have been initialized - which is required for the CLR to pass your code as safe/verified .
See Also: CLR via C# 2nd Ed - Pg 188
I just found an explanation in the MSDN forum stating that this rule is enforced because zeroing out the memory is skipped if you use a none default constructor. So you will have to provide initialization values for all fields in order to avoid some fields containing random values. You achieve this easily be calling the parameter less default constructor, but at the cost of initializing some fields twice.
I cannot tell if this explanation is correct, but it sounds reasonable.
When you define a non-default initializer, C# requires you to set all fields because it
skips the zeroing of memory and lets you initialize it - otherwise you'd have to have a
double initialization performance hit. If you don't care about the (very slight)
performance hit you can always chain a call to the : this() initializer and then only
initialize selected fields.
This is because structs are derived from System.ValueType and not System.Object, System.ValueType implements default constructur which you cannnot override, this default constructer initializes all fields in struct with its default value. So if you are implementing any parameter contructor in your class you will also need t0 ensure you invoke system.ValueType default const. And to answer why it needs to init all its value this is because value are stored in stack memory.
This works:
public Direction(int azimuth)
{
_azimuth = azimuth;
}
From the spec:
Struct constructors are invoked with
the new operator, but that does not
imply that memory is being allocated.
Instead of dynamically allocating an
object and returning a reference to
it, a struct constructor simply
returns the struct value itself
(typically in a temporary location on
the stack), and this value is then
copied as necessary.
Basically, the compiler must see that every field gets initialized in the constructor so that it can copy those values, and it is not willing to examine calls to functions or properties.
public struct Direction
{
public int Azimuth { get; private set; }
public Direction(int azimuth) : this()
{
Azimuth = azimuth;
}
}
You need to initialize the field, and not via the property.