I normally declare an optional parameter (default value) in the Constructor's parameters like this:
public class XV
{
public XV(int startPosition = 1) // Constructor
{
this.StartPosition = startPosition;
}
public int StartPosition { get; set; } // Property
}
But then I found myself looking at old code where I've written like this:
public class XV
{
public XV( ... ) { ... } // Constructor
public XV(int startPosition) // Constructor
{
this.StartPosition = startPosition;
}
public int StartPosition { get; set; } = 1; // Property
}
What is the use of adding = 1 to the Property compared to creating an optional value in the Constructor?
Is it just to support default values for Properties not present in the Constructor?
But if so, why not add them to the Constructor in that case?
With a optional argument (1st case), you can call the function without giving a value.
new XV(); would be a valid call to a function with a optional parameter. Even if you did not define a parameterless constructor. The constructor signature would match calls () and (int). Indeed, having the ability to match more calls with one signature is a big use case for Optional Arguments.
Note that optional parameters were not around all the time. They were only added back in C# 4.0. So old code might not use it, because it simply was not a thing back then. However for constructors, constructor chaining was often used to define de-facto default values.
In the 2nd case, only (int) calls would be valid. If you wanted to use the default value, you would have to retrieve it first. So it is hardly what I would call a "default value". "Reliable initialization" would be a better term. It is indeed useless and might be cut entirely by the compiler and JIT.
If you wanted a parameterless constructor, you would have to explicitly code one (classes start with a implicit parameterless one, but any explicit constructor disables that).
Related
I have this code:
public class NewFrame
{
public NewFrame(string iconSource = Const.Car,
string iconColor = Const.Red)
{
When I try and use it then it's telling me I am missing a default constructor. How can I add one of these and still make the code use the default values for iconBackgroundColor and IconSource? I thought that adding in those defaults with the = Const. would make it work but it seems like it doesn't think my constructor is a default (with no params).
You just have to add another empty overload and call the required constructor with defaults. See below:
public class NewFrame
{
public NewFrame() : this(Const.Car, Const.Red){
}
public NewFrame(string iconSource,
string iconColor)
{
...
}
}
By having two optional parameters, you don't actually create 4 different constructor declarations under the hood (one with both parameters, one with the first parameter, one with the second parameter, and one with neither). There is still only one constructor, with two parameters. It's just that C# recognises that the parameters are optional, and has syntactic sugar to let you omit them when you call the constructor.
However, if you use reflection to create an instance of your class (probably whatever the thing that requires a default constructor is doing), and you attempt to invoke the parameterless constructor, it won't find one, because there is no syntactic sugar in reflection.
Here is an example:
class MainClass
{
public static void Main(string[] args)
{
Type t = typeof(MainClass);
object o = Activator.CreateInstance(t, 1);
Console.WriteLine(o);
}
public MainClass(int a = 10)
{
}
}
If you use typeof(MainClass).GetConstructors(), it will tell you that there is only one.
To actually declare a default constructor, you can do:
public class NewFrame
{
public NewFrame(string iconSource = Const.Car,
string iconColor = Const.Red)
{
...
}
public NewFrame() : this(Const.Car, Const.Red) { }
}
For what it's worth, when I do something like this, I take the route that #VyacheslavBenedichuk's answer is showing.
I'm not sure what your complaint is. This code compiles for me:
public class TestConstructor
{
public TestConstructor(string what = Const.Car, string color = Const.Red)
{
}
public static void Test()
{
var tc = new TestConstructor();
}
public class Const
{
public const string Car = "car";
public const string Red = "red";
}
}
What do your definitions of Const.Car and Const.Red look like? Where are you seeing the error?
But, if you use something that requires a default constructor, then this will not work. For example, this will fail at runtime:
var tc2 = Activator.CreateInstance(typeof(TestConstructor));
Please, when you are reporting an error, describe it exactly - in particular say whether it's a runtime or a compile-time error, the exact wording of the error, and the context in which the error occurs. In this case (the call to CreateInstance) will result in a System.MissingMethodException: 'No parameterless constructor defined for this object.'
In this case, you need to follow #VyacheslavBenedichuk's advice
In C# 9, one can define a property with the same name in a record both in its primary constructor and in its body:
record Cat(int PawCount)
{
public int PawCount { get; init; }
}
This code compiles without errors.
When initializing an instance of such a record, the value provided to the constructor is completely ignored:
Console.WriteLine(new Cat(4));
Console.WriteLine(new Cat(4) { PawCount = 1 });
prints
Cat { PawCount = 0 }
Cat { PawCount = 1 }
Is this behavior correct or is it a bug?
If it’s correct, what are the cases in which it is useful?
I expected the compiler to either reject this code with an error like ‘The type Cat already contains a definition for PawCount’ or consider the property in the constructor and in the body the same, performing its initialization from the constructor.
The latter variant could be useful to provide the property with a custom getter and/or initializer without having to rewrite all the properties of the positional record in its body.
The actual behavior makes no sense to me.
The correct way to do this is:
record Cat(int PawCount)
{
public int PawCount { get; init; } = PawCount;
}
This is useful as it allows you to do e.g. validation
record Cat(int PawCount)
{
private int _pawCount;
public int PawCount {
get => _pawCount;
init => _pawCount = value < 0 ? throw new ArgumentException() : value;
} = PawCount;
}
The spec for this is here: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#members-of-a-record-type
Members are synthesized unless a member with a "matching" signature is declared in the record body or an accessible concrete non-virtual member with a "matching" signature is inherited. Two members are considered matching if they have the same signature or would be considered "hiding" in an inheritance scenario.
So since a property with the same name as the parameter already exists, the compiler wont synthesize a PawCount property, and so just silently ignores the parameter unless you use it yourself explicitly.
I have a simple question.
Assume that I have class like below.
public class DamageToDeal
{
public bool enabled;
public float value;
public TDValue type;
public DamageToDeal() { }
public DamageToDeal(bool _enabled, float _value, TDValue _type)
{
enabled = _enabled;
value = _value;
type = _type;
}
}
I read that if I have custom constructor the default is not automaticaly generetared
Do I have to initialize fields myself with default values(0, null) or default constructor with empty body will do it anyway?
Or if the default constructor is initializing fields even if he has empty body ?
Memory allocated to a new class instance is cleared by the memory allocator. You only have to ensure that any fields you want to have a non-default value is assigned.
This is documented here: Fundamentals of Garbage Collection:
Managed objects automatically get clean content to start with, so their constructors do not have to initialize every data field.
You do not need an empty parameterless constructor for this to happen. You would only add that constructor if you actually want to call it and that makes sense for the type.
Also note that any field declarations that also states an initialization expression is lifted into constructors.
If you do this:
public TDValue type = new TDValue();
then regardless of which constructor is called that field will have an instance reference to a new TDValue object.
Note that the above is valid for classes, not for structs. For structs you need to ensure you assign all fields because memory is not always "allocated", it might be just reserved on the stack.
I have the following bit of code:
public struct Interval
{
public double Min { get; set; }
public double Max { get; set; }
public Interval(double min = double.MinValue, double max = double.MaxValue)
{
Min = min;
Max = max;
}
}
The compiler is complaining that
Backing field for automatically
implemented property must be fully
assigned before control is returned to
the caller. Consider calling the
default constructor from a constructor
initializer.
Which is something that I don't understand, since my constructor is fully initializing the values of this struct. Isn't it?
Your constructor is trying to set properties - which it can't do until it knows that all the fields have been initialized. (You can't call any instance methods or access any properties until all the struct's fields are definitely assigned.) It's a quirk which manifests itself when you're using automatically implemented properties: you have fields that you can't access other than via the property, but you can't use the property before assigning the field a value! The fix is simple - just add a call to the parameterless constructor:
public Interval(double min = double.MinValue, double max = double.MaxValue)
: this()
{
Min = min;
Max = max;
}
This works because the parameterless constructor assigns the default values to all fields, after which you can use the properties without any problems.
However, I'd recommend against using mutable structs in the first place.
Heed the error message and add a call to the default constructor like so:
public Interval(
double min = double.MinValue,
double max = double.MaxValue
)
: this() {
Min = min;
Max = max;
}
The issue is that as written the backing fields aren't initialized; that makes the compiler very unhappy. However, the default parameterless constructor will initialize these fields for you which is why the problem disappears when we chain a call to that constructor.
Add :this() to the ctor:
public Interval(...args...) : this() {
... Code ...
}
I just wrote this few days back.
http://www.abhisheksur.com/2010/10/hidden-facts-on-c-constructor-in.html
If you write a constructor for your structure, you need to initialize each and every member of your struct before returning from the constructor. Backing fields might be creating some issue with your code, you can fix it using the call to Default constructor.
this()
It compiles just fine for me: http://ideone.com/mgBpt
What version of C# and compiler are you using?
Based on my question - take the following code:
class Nevermore60Customer: GenericCustomer
{
public Nevermore60Customer(string name, string referrerName)
: base (name)
{
this.referrerName = referrerName;
}
private string referrerName;
private uint highCostMinutesUsed;
To me, it appears the variable "referrrerName" is being initialized "after" it is being referenced as a passed parameter in the constructor.
public Nevermore60Customer(string name, string referrerName)
Am I worng, and if so how? Or if I am right and it is being initialized after it is being referenced in the constructor, how is it possible?
Thanks
The position of the variable declaration compared with the constructor is irrelevant to C#.
It would make this easier to discuss if you had different names for the parameter and field though:
class Test
{
public Test(string parameter)
{
this.field = parameter;
}
private string field;
}
Basically the field "exists" before the constructor is called. If the field is declared with an initializer, like this:
private string field = "default value";
then that initializer is run before the constructor, even though it may come after it within the source code.
The constructor argument is not an alias for the field. It hides the field name, this code won't work:
public Nevermore60Customer(string name, string referrerName) : base (name)
{
referrerName = referrerName; // bad
}
By using the "this." prefix, you can tell the compiler to assign the argument value to the field. It is a very common pattern, avoids having to come up with another name for the argument. Or do something awkward like prefixing the field name with, say, an underscore.
Not sure I understand the question. Your constructor has a strign parameter, referrerName, that you are assigning TO a private class variable, also called referrerName. I don't see where this.referrerName is referenced before its initialization?
this.referrerName refers to the class member declared as private string referrerName;
The referrerName to the right of the = is the parameter to the constructor.
It doesn't matter how you order the private members of the class and the constructor, the private members will always be initialized first.
C# is an object oriented language and you seem to confuse plain C procedural language concepts with C#. Unlike C, in C# the order of declaration does not matter as long as the instance is initialized before accessing and is within the scope.