Open gafter opened 7 years ago
@jnm2 I don't think that it's about the benefit but more of a consistency so yes symmetry but dunno what it means for the run-time, I'm not sure what would happen when you define a parameterless constructor and set the fields, I voted because I hope that we could have default values but dunno.
Downvoting this as per my comment for Roslyn #13921. For a struct, S
, if default(S)
and new S()
leads to different values, then this feature has no real benefit and will cause serious bugs. This should be implemented properly in the CLR before it's considered for C#.
I agree. If we're going to get non-nullable reference types and structs with parameterless constructors, then we should be able to override default
for them,
I really hope that they will design and implement this properly.
To play Devil's advocate, why is it expected that new struct()
and default(struct)
produce the same value? That's never been the case with reference types.
Does C# support consuming structs with default constructors today? You could always define one using IL or some other language. How about the IL that C# produces for a generic method with a new
constraint? Would that work today with such a type?
The concept of a "default" for struct types is interesting but even in the case of a blittable default you're talking about adding some serious overhead.
@HaloFour Well, it might be possible to mitigate the overhead by an attribute? like opt-in/out?
@HaloFour,
Your devil's advocate is a good question. To my mind, the answer is "and that is exactly what's wrong with reference types". It is the driver behind non-nullable reference types. SO just at the time when we are looking to address the "null problem", we'd be introducing the same problem in to structs.
@eyalsk @DavidArno
I think we're wading into the territory of a different discussion here.
I just tested and answered my questions. C# does currently support consuming structs that define default constructors. It just doesn't support defining them. There is one notable exception and that's in a generic method where the type has a constructor constraint. I believe that this was already considered a bug in the JIT.
We would need the core clr bug https://github.com/dotnet/coreclr/issues/6843 fixed before we could do this.
What down-to-earth benefits would we gain from this proposal? I'm inclined to say this raises the complexity of the mental model with no benefits in sight, since no one in Codeplex, /roslyn or this thread has mentioned a single pragmatic benefit despite people repeatedly asking.
@jnm2 What do you mean? are you saying that it has zero benefit?
Just as a real world example, I have a text parser where the scanner returns a struct that looks like this:
public TextChar(char value, int index = 0, int lineNumber = 1, int columnNumber = 1)
{
// ...
}
Now, when you create a new instance of the struct or use default lineNumber
and columnNumber
should default to 1 when there are no characters at all but atm it default to 0.
Update: Rephrased.
@jnm2,
Let's say I have a type that holds information on a car gearbox, including the number of gear stick positions and the gearing ratios and there will always be at least two positions (forward and reverse).
Currently, my options are to use a class (so I can control its construction) and suffer null ref problems, or have a struct. The latter then either suffers the equivalent of the null ref problem if created with default(Gearbox)
or new Gearbox()
, or I have to leave all the fields as mutable and, on all methods and properties of the struct, I have to put guard code that checks for a misconstruction and sets up a proper default set of values.
If this feature were correctly implemented, eg via an overridable default
method in the struct, then I could set those default values (or even throw some "not properly constructed" exception at the point of creation) when default(Gearbox)
or new Gearbox()
is called.
As the proposal currently stands though, default(Gearbox)
wouldn't call the constructor, so from my point of view it doesn't achieve anything useful: I'd still need that guard code or would still need to use a class.
BTW, some people workaround this by doing something like the following:
struct S
{
private int _lineNumber;
public S(int lineNumber = 1)
{
_lineNumber = lineNumber;
}
public int LineNumber => _lineNumber == default(int) ? 1 : _lineNumber;
}
Now, this can work but doing this for every field is just boilerplate.
@DavidArno default is not a call. Default means zero-fill. Default happens when you create an array. Default happens to all class fields before your initialization code runs. Default happens due to a number of things, and it's for safety so that you don't get junk. Treating it like a call makes no sense because it isn't a call. We need it to be what it is: zero-fill.
I would expect that new S()
could be different from default(S)
.
That means that developers should be aware that their struct can be created without running any constructor.
@eyalsk same thing about default. It means "I want this memory to be zero" and changing that semantic would be next to impossible, let alone desirable.
I'm not sure I follow you with the rest of what you showed. Those parameter defaults should only apply if you use that constructor. new TextChar()
will not use the constructor because you did not specify the non-optional value
, so you would not expect the other parameter defaults to apply since those parameters do not come into play since you chose not to use that constructor.
I'd like to reiterate that this proposal has nothing to do with changing how the C# compiler interprets new T()
where T
is a struct
. That behavior is already means something different from default(T)
and, as far as I'm aware, that has been the case since C# 1.0. To the CLR, and even to C#, new T()
and default(T)
have never meant the same thing.
Of course since neither C# nor VB.NET permitted defining a struct with a parameterless constructor the odds of running into one would be exceptionally low. So low that a bug made it into the BCL assuming that such a beast wouldn't exist. But it's quite possible for some other language to define such a struct, and C# already respects such structs by invoking their constructors.
So, in my opinion we need to move on from the conversation about whether structs should have parameterless constructors or whether C# should consider new T()
and default(T)
to be two different things. That ship has apparently sailed a long time ago. At this point the question should be only whether C# should permit defining new structs with parameterless constructors. Now, if you don't like the idea of parameterless constructors in structs and don't want Microsoft to encourage writing them I definitely think that's a fair argument.
@jnm2
I'm not sure I follow you with the rest of what you showed. Those parameter defaults should only apply if you use that constructor. new TextChar() will not use the constructor because you did not specify the non-optional value, so you would not expect the other parameter defaults to apply since those parameters do not come into play since you chose not to use that constructor.
Let's disregard my previous post because it wasn't clear so let's focus on the following examples instead:
public struct Line
{
public Line(int lineNumber = 1)
{
LineNumber = lineNumber;
}
public int LineNumber { get; }
}
Possible today but when you do new TextChar()
then LineNumber
results 0 instead of 1.
public struct Line
{
private readonly int _lineNumber = 1;
public Line(int lineNumber)
{
_lineNumber = lineNumber;
}
public int LineNumber => _lineNumber;
}
Not possible today but I'd like to have it so in the case of the following code:
public struct TextChar
{
private readonly int _lineNumber = 1;
private readonly int _columnNumber = 1;
public TextChar(char value, int index = 0, int lineNumber = 1, int columnNumber = 1)
{
Value = value;
Index = index;
_lineNumber = lineNumber;
_columnNumber = columnNumber;
}
public int ColumnNumber => _columnNumber;
public int Index { get; }
public int LineNumber => _lineNumber;
public char Value { get; }
}
When I'd do new TextChar()
then LineNumber
and ColumnNumber
would result 1 as opposed to 0.
Now, it's a not a deal breaker but I think that having the ability to do it will fix the slight disparity with classes and people won't need to circumvent or fight the language and do something like this:
public int LineNumber => _lineNumber == default(int) ? 1 : _lineNumber;
@HaloFour,
Currently, New S()
and default(S)
do the same thing in all .NET languages, due to the optimization introduced in Activator.CreateInstance. That comes about because the dominant .NET languages didn't allow custom parameterless constructors, so there was no point in calling it for structs. Because of that, lots of code will have been written that uses default(S)
instead of new S()
, because they currently do exactly the same thing. As far as I can see therefore, "fixing" that optimisation, and allowing the two struct creation options to behave differently could prove a huge breaking change for many folk.
@jmn2,
If non-nullable reference types are introduced, then for a non-nullable class, C
, what would you expect the result of default(S)
to be? "Zero-filled" null
? That seems broken to me as suddenly my non-nullable class as null
reference, which just broke my code. Likewise with structs that implement parameterless constructors. Those constructors are being implemented because a zero-filled default doesn't make sense for that type, therefore a zero-filled default(S)
would be an invalid value for that struct.
@DavidArno
Currently,
new S()
anddefault(S)
do the same thing in all .NET languages, due to the optimization introduced in Activator.CreateInstance.
That only affects specific uses with generics. When using a struct directly the C# compiler will always invoke the parameterless constructor if it exists. That is also being regarded as a bug in the CLR which at the time of implementation was itself a breaking change.
Even though I do largely use the generic type argument syntax, I am referring to all uses of structs in my argument. If you were handed an assembly the contained a struct called FooStruct
and you used new FooStruct()
that never implied the same thing as default(FooStruct)
.
void Test1() {
var s = new FooStruct(); // invokes parameterless constructor
}
void Test2<T>() where T : struct {
var s = new T(); // invokes parameterless constructor
}
void Test3<T>() where T : new() {
var s = new T(); // does not invoke parameterless constructor
}
As mentioned in https://github.com/dotnet/coreclr/issues/6843, structs with parameterless constructors are a part of the CLI spec. As far as I can tell they also meet CLS requirements. C# is required to at least respect them. So is the CLR.
@eyalsk So all you are really asking for is struct field initializers. (Field initializers would be possible if you call new Struct()
but impossible if you use default(Struct)
because, as previously mentioned, default(T)
is defined as zeroing the containing memory space.)
I suppose it's a matter of perspective. Since default(Struct)
is not going away, nor should it, you'll still have to deal with (new Struct[1])[0]
zero-initializing all the fields and you'll still have to harden your struct against zero-inited fields. Since you'll still always have to handle zero-init fields, are field initializers that much of a win? Field initializers can only work if you call a constructor. Creating an array does not call constructors for each element.
@jnm2 I can relate to what @DavidArno says but at the same time I wouldn't mind that new S()
and default(S)
would result a different value.
I wasn't speaking about default
and as @HaloFour said we shouldn't derail the discussion as it should be discussed in a new issue so I didn't mention it.
are field initializers that much of a win? Field initializers can only work if you call a constructor. Creating an array does not call constructors for each element.
You're right but at the same time it depends: in my case I'm not returning an array but an Enumerable<TextChar>
and all the instance of TextChar
are being properly initialized and it's only happens in one place this is the reason I wrote that for me, it isn't a deal breaker, it's just nice to have.
Beyond that I think that it might be nice to fix the slight disparity between classes and structs that might be a surprise to new comers.
@eyalsk default
is a necessary part of this conversation. I asked what the benefit of this proposal is; you answered that the benefit is not having to "fight the language" by worrying about zero-inited fields.
But because default
will happen to every struct, therefore you must harden your field logic against zero-init. Because you must harden your field logic against zero-init, this proposal does not alleviate any worry about zero-inited fields. Therefore, that's not a benefit. Unless some other benefit is proposed, this proposal is at -100. :')
@jnm2 I've updated my last post with some more info, I don't know why default
is necessary part of this conversation, it's a different thing and as such should be discussed in a new issue.
The discussion about default
just "hammer" this proposal down for no reason at all, features should stand on their own and as such discussed separately.
default
and new()
for class is difference
So we can assume the same for struct. new Struct() is explicit construct, default(Struct) will be zero memory. And that's normal behaviour
If non-nullable reference types are introduced, then for a non-nullable class,
C
, what would you expect the result ofdefault(S)
to be? "Zero-filled"null
? That seems broken to me as suddenly my non-nullable class as null reference, which just broke my code.
I presume you meant to use default(C)
there. And well, are you talking about the current thing that really only warns against potential null
-derefs at design/compile-time, or are you holding out for some brand new CLR + language version where you could state a class can't be null
at its declaration rather than at the individual use-sites?
@Joe4evr,
Yes, I did mean default(C)
. Thanks for the correction.
Like with this feature, only providing compile-time checks would make it a leaky feature, prone to bugs. So yes, I'd want to see it properly implemented in the CLR, not just in the language.
However, it's clear that I'm in the minority here when it comes to structs, so I'm likely in the minority on that feature too. I can't win them all...
Even then, all other languages I heard about that have non-nullable classes have it so you specify whether or not null
is an accepted value at the use-site, not at its declaration. And I guess that would be because the latter option would be inhibiting composability.
@DavidArno If we have non-nullable type. I would prefer that it will be compile time feature you will never be able to create collection of it from new array (at compile time)
But instead you need to convert from another collection (such as by linq) and make sure it will never be null
string![] NonNullable = new string![10]; // ERROR
string![] NonNullable = Enumerable.Repeat((string!)"",10).ToArray(); // Pass
string![] NonNullable = new string![] { "","","","","","","","","","" }; // Pass
I wish all feature like that should only be compile time. We just need to make sure we protect everywhere, such as array of non nullable object like you concern, to avoid bug like that
This is a real pain point when trying to use struct
as non-nullable wrappers for more complex types. In my functional library I have a ton of immutable collection types (Lists, Maps, Sets, etc.). They are all structs that contain a single reference to a class
with the full implementation. It means I can have:
Map<string, int> x = default(Map<string, int>);
But that then requires a null
check every time the type is used:
public struct Map<A, B>
{
readonly MapInternal<A, B> value;
// This property is used throughout the type to get a valid MapInternal
MapInternal<A, B> Value => value ?? MapInternal<A, B>.Empty;
// .. impl ...
}
Obviously this is still better than forcing the programmer to do it manually, but less efficient overall.
I seem to remember this being brought up for C#6 and was dropped, so I'd be interested to know why?
Right now no struct
will compile with a default constructor, and existing structs can continue working as they are, with the same semantics for default instantiation and copying. There shouldn't be any need to change any layout behaviours, array layout behaviour, default zeroing behaviour for structs that have no default constructor.
For types with a default constructor then I expect it only to be called when the type is instantiated via new
or default(T)
, any copying semantics should stay the same and not call the default constructor.
How would you not have to still check for null? What about default(T)
and (new T[1])[0]
, etc?
You're always going to have to check for null. You would have to check for uninitialization if T was a reference type too, but the runtime does that for you and it does not do that for value types. That's what makes the difference; not whether you can define a default constructor.
@jnm2 The default constructor can set the internal value, that's the point. If default(T)
doesn't call the default constructor (or some provided Default
override method) then there is literally no point doing this feature in my opinion. There's a massive hole in the type-system which is that certain types can construct without being constructed. I get it for numeric types where blitting the memory with zeros will give you the right result, but for many other things it's problematic. For example I have an Either<L, R>
type in my functional library that is a struct, yet I have to rely on having a status field that is an enum with IsUninitialised
as the 0
item just in case it gets magicked into existence.
Personally I'd like to see C# take steps to help us get away from garbage undefined types. I understand that default(T)
is a problem semantically, but perhaps making it a compile time error for structs of T
with default constructors would be OK. But tbh I think it should just call the default constructor, because unlike reference types, default(T)
isn't null
, default(T)
is a fully constructed type with fields, and methods that will appear to work and it's functionally equal to new T()
already.
I see all the comments about how difficult this is. But I find excuses like that to be pretty shallow. I can't accept that doing the right thing and making the language more type-safe is ever not worth doing. The two major holes are uninitialised structs and nullable references. I'd be happy to see no other feature for a decade if those two constant thorns were to die.
@louthy
This proposal isn't about whether or not structs with default constructors can exist in the CLR type system. They already can, and have been supported since 1.0.
This proposal also isn't about how C# supports those types if it ever comes across them. The language already completely supports invoking those parameterless constructors if they exist and already treats default(T)
and new T()
as completely separate things when applied to structs.
This proposal is only about whether C# as a language will support letting people define new structs with parameterless constructors. Everything else will remain as it is as any additional changes would break existing behavior.
As previously expressed, I completely agree with @louthy that this proposal - allowing new S()
and default(S)
, where S
is a struct, to end up as different values - literally has no point. This proposal only makes sense if a means of setting non-zero/null defaults can be achieved via default
too.
Not doing this would be the true breaking change as there is likely lots of code out there that currently treats new S()
and default(S)
as equivalent and this proposal would break those assumptions.
@HaloFour You may have a clearly defined idea of what you think this feature should be. Please don't stop me from expressing what I feel is a major issue with structs and the proposal to leave default(T)
behaviour alone.
Adding parameter-less constructors comes with a ton of baggage that should be discussed:
i.e.
public class Foo
{
public Bar X; // Bar is a struct, does it call new Bar() or default(Bar)
}
var x = new Bar [100]; // Does it call new Bar() or default(Bar) foreach item?
If the types (with parameter-less constructors) can still be constructed in a broken state, then there's no point to this feature at all. The point of a parameter-less constructor (in general terms) is to provide a safe default value. The language/CLR ignoring that is insane.
@louthy @DavidArno
Not doing this would be the true breaking change as there is likely lots of code out there that currently treats
new S()
anddefault(S)
as equivalent and this proposal would break those assumptions.
This proposal doesn't change anything about what default(T)
and new T()
do, that's my point. That decision was made 15 years ago. They've always been different, both for reference and value types. C# has always respected structs with parameterless constructors, it's just never allowed making them. But you could always make them with IL or other languages, and C# would be perfectly happy to invoke that constructor if you used new T()
.
The rest of the discussion around default(T)
is something else entirely. There are and have never been "calls" there. There is only zero-initialization, which is what default(T)
means regardless of what T
is. This is baked into the CLR. You request an array of size x
and the CLR zero-inits a block of memory to fit the elements. It doesn't really care what you're sticking in there or what members that might have (if any).
If you want to argue the semantics of default(T)
in C# and the CLR there are other proposals for that. But you're going to have to solve the issues of initialization of unknown/generic types.
@HaloFour
The rest of the discussion around
default(T)
is something else entirely. There are and have never been "calls" there. There is only zero-initialization, which is whatdefault(T)
means regardless of what T is. This is baked into the CLR.
What C# emits for default(T)
is not baked into the CLR. It simply chooses to emit something that is baked into the CLR, but could very well emit a call to Activator.CreateDefaultInstance()
that would actually check if there was a parameterless constructor and call it.
@HaloFour I appreciate what you're saying, but just as default(T)
may have a well defined role in the CLR, it is also a language feature in C#. The language-feature isn't immutable, even if the CLR is. The compiler replacing default(T)
with new T()
when a struct
has a parameter-less constructor isn't going to break any existing C# code (perhaps instantiating types from other languages, but I'm not sure on that, happy to discuss that). It could also see an array allocation and call new T()
n
times post allocation to populate the array (again, only if the struct
has a parameter-less constructor).
It seems to me that giving the programmers of C# the tools to make this decision in favour of type-integrity (over a loss in performance) is essential.
If there's going to be a major problem of types written in F# or VB then perhaps some annotation (attribute or keyword) that allows us to mark a struct
as "Yes, I really care about initialising this type", would be the way forward. Either way I shouldn't have to build my own IL to get a safe struct
.
@orthoxerox
Indeed, you then have to solve that problem across the entirety of the CLR where "default" is assumed to mean zero-init. Generics, arrays, stack allocation, unmanaged code, etc. Good luck with that.
Either way, that's not particularly relevant to whether or not C# allows defining new structs with parameterless constructors. That's a simple CLR-supported and CLS-endorsed language feature.
The actual case is default(T)
is zeromemory. Which always faster than calling constructor. Not to mention your custom constructor
Especially Array of struct. To make a chunk of zeromemory is faster than calling constructor for each one
struct should be fast. So I am very against the idea that calling constructor on struct array
@Thaina This isn't suggesting removing that feature for existing structs. It's only suggesting removing that feature for structs with parameterless constructors (which currently can't be authored in C#). The programmer can decide to provide a parameterless constructor or not, and therefore decide whether to take the performance hit (to benefit from types with integrity) or not. There isn't going to be a general performance hit to any existing structs.
To realize this feature without running into the default(Struct)
problem, while maintaining wide backwards compatibility I propose the following approach.
(This is no new issue, because it absolutely fits in this proposal)
When forcing structs to be nullable-types only, the problem with default(Struct)
is actually gone, because that will be null
then. During creation, the fields are initialzed and a custom non-args constructor is called.
struct? Fraction
{
public double Numerator;
public double Denominator = 1.0;
public Fraction() { Log("..."); }
}
used like
Fraction? frac1; // is null / default(Fraction?)
Fraction? frac2 = new Fraction(); // initialized
Fraction? frac3 = new Fraction?() // is null / default(Fraction?)
Fraction? frac4 = default(Fraction?) // dto.
Fraction frac5; // error: must be nullable
Fraction frac6 = new Fraction(); // error: must be nullable
Fraction frac7 = default(Fraction) // error: cannot create default for forced nullable
GetUnderlyingType
and Value
do return the non-nullable type, but only for internal purposes, because explicitly declaring a variable of that underlying non-nullable struct is not possible. The exception is when using reflection or e.g. "violating" calls to Activator.CreateInstance
.
Custom conversion operators are possible, but they must respect the structs interal values anyhow to be meaningful.
Internally the struct is decorated with a ForcedNullableStructAttribute
.
[ForcedNullableStruct]
struct Foo { }
For a consistent look-and-feel with Nullables this attribute is attached by declaring the struct with struct?
Calling the constructor is accomplished by (internally) creating a constructor
public Foo(ForcedNullableStructAttribute fnsa)
{
/* field initialisations and custom non-arg constructor goes here */
this._isInitialized = true; //see below
}
This constructor is called by the produced MSIL after the instanciation of a struct?
. Field initialisations take place at the beginning of the constructor call, like for classes now.
Custom constructors are internally rewritten to
public Foo(double X, double Y) : this( default(ForcedNullableStructAttribute) )
{
// custom code
}
Correct usage of this Forced Nullable Struct is enforced by the compilers. Due to the CLR compatible realisation other .NET languages can make use of the structs, though they must pay attention to the nullable character. Therefore an IsInitialized
property is added to every struct?
, that is set to true
when the initializing constructor is called.
struct? Foo
{
private readonly bool _isInitialized;
public bool IsInitialized => _isInitialized;
/ *** /
}
Following forced nullable struct is given:
public struct? FNPoint
{
public double X;
public double Y;
public double Z = 1.0;
public Point(double X, double Y) { this.X = X; this.Y = Y }
public static string ToString(FNPoint? point)
=> (point.HasValue) ? $"({point?.X}, { point?.Y}, {point?.Z})" : "(null)";
}
In VB.NET this forced nullable struct can also be used, even as a standard-struct.
Dim point = New FNPoint()
Console.WriteLine("Point is initialized: " & point.IsInitialized)
Console.WriteLine($"(X, Y, Z) is {FNPoint.ToString(point)}")
' using it beyond this point is possible, but pointlesss
point = New FNPoint(CType(Nothing, ForcedNullableStructAttribute))
Console.WriteLine("Point is initialized: " & point.IsInitialized)
Console.WriteLine($"(X, Y, Z) is {FNPoint.ToString(point)}")
' the above point can safely be handed around
point = New FNPoint(2.0, 3.0)
Console.WriteLine("Point is initialized: " & point.IsInitialized)
Console.WriteLine($"(X, Y, Z) is {FNPoint.ToString(point)}")
' the above point can safely be handed around
When the point
from VB is passed to the static ToString()
function, it is implicitly converted to a nullable, because the signature requires so. A signature of ToString(FNPoint point)
is not allowed by the compiler for forced nullable structs.
Shall an automatically generated method "InitializeForcedNullableStruct()" be available for other languages?
struct? Foo {
public bool IsInitialized { get; }
public void InitializeForcedNullableStruct()
{
if (IsInitialized) throw new NotSupportedException();
/* stuff from the custom non-arg-constructor goes here */
// the next field is only accessible by the compiler
<IsInitialized>k__backingField = true;
}
public Foo(ForcedNullableStructAttribute fnsa)
=> InitializeForcedNullableStruct();
}
ForcedNullableStructAttribute
is not re-used by custom code?@lachbaer,
How is that an improvement? You have taken a problem (can't non-zero initialise a struct) and wrapped it in a class and thus introduced null into the mix. Further, as this is just a compiler trick, you now have _isInitialized
, that must be checked by each member to ensure correct initialisation when used with other languages. Why would I not just have a struct with _isInitialized
, or just make Fraction
a class when we get non-nullable references and avoid all this mess?
@DavidArno At least it is backwards compatible and practical.
or just make Fraction a class when we get non-nullable references and avoid all this mess
I recommend you study http://stackoverflow.com/questions/85553/when-should-i-use-a-struct-instead-of-a-class 🤣
@lachbaer,
Ha, if only the language team had studied that (in particular "Do not define a structure unless the type ... is immutable") when designing value tuples.
For me, the primary reason for considering structs is the fact that they protect code from null. The primary reason for giving up on them 10 seconds later is because I invariably need non-zero initialization of one or more fields (often to avoid null). Your proposal breaks the first consideration and only achieves the second through the need for an initialized-check overhead. So it's not practical from my perspective. It's not so much backwards compatible, as it's a step backwards...
@DavidArno Have you seen my comment @ https://github.com/dotnet/csharplang/issues/460#issuecomment-295661489 already?
~My idea here is not a step backwards. It is as legal as any other idea. I even put some effort in it to prove the concept. It's really a bit rough of you to tell it (objectively) a "step backwards" and just giving a discussable argument that is introduced with "For me, the primary reason [...]" That is like slamming me in the face! Do you do that to others participants as well? Sorry, that others don't have an IQ of 180, like you obvious think you have... - I digress. Personal comments should not belong here, just couldn't resist this time~ 😶
One can however argument, that this is too much overhead for a struct
and makes it too heavy, as a struct should be lightweight. There are probably only some limited use cases for it. And by creating a pseudo-argument constructor you can achieve nearly the same.
[MyAnalyzer.CallPseudoConstructor]
struct Fraction {
public double Numerator;
public double Denominator;
public Fraction(int Pseudo) {
Log("Fraction created");
Numerator = 0.0;
Denominator = 1.0;
}
public Fraction(double num, double, denom) : Fraction(0)
{ Numerator = num; Denominator = denom; }
}
Fraction frac1 = new Fraction(0);
Fraction frac2 = new Fraction(1.0, 1.0);
Fraction frac3; // Analyzer gives error, because no constructor is called
Fraction frac4 = new Fraction();
// Analyzer gives error, because at least pseude constructor must be called
Together with a new CLR supported type construct "cluct
" or "strass
" 😁 as initially mentioned in https://github.com/dotnet/csharplang/issues/460#issuecomment-295661489 the requests of this proposal can be served, as well as other issues.
@jnm2 Can you explain why you downvoted on the previous post?
Can you explain why you downvoted on the previous post?
cluct
and strass
, sorry.@jnm2 I re-read the objection and I must correct myself.
@DavidArno I am really sorry for the offense! I previously didn't get the meaning of your statement, though reading it several times before I started commenting. I felt it was very offensive to me and my view on things, taken into account other comments to me of yours. This was a misunderstanding because of language misinterpretation (here we go again => human factors 😉 ) Please accept my apologies.
See also https://github.com/dotnet/roslyn/issues/13921 https://github.com/dotnet/roslyn/issues/1029
LDM history: