Replies: 25 comments 12 replies
-
Considering that the copy behavior is the one major defining trait of |
Beta Was this translation helpful? Give feedback.
-
So why is that a struct when it should logically be a class? And you can forget using "muh allocations" as your answer. In Eric Lippert's words:
|
Beta Was this translation helpful? Give feedback.
-
What about Swift behaviour that a struct is only copied when it is written to:
|
Beta Was this translation helpful? Give feedback.
-
That's not automatic in Swift. That language has the same semantics as C#. To make a struct use CoW semantics you have to wrap it in a reference type. It's similar to String in the BCL. It is a reference type but also effectively immutable and modifications are performed through methods which copy and mutate the value. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour If it were to be automatic, it would be very beneficial, and I don't think it would break existing code. |
Beta Was this translation helpful? Give feedback.
-
Doesn't matter what mechanism of clean up that it's tied to, it still involves an allocation on the heap and passing that reference around. Doing such would have a drastic impact on the performance of existing code. Doing such automatically doesn't make sense in any language, including Swift. You either have reference semantics with shared state or you have value semantics with copying. |
Beta Was this translation helpful? Give feedback.
-
Why is |
Beta Was this translation helpful? Give feedback.
-
You may have a struct: struct Size
{
int x;
int y;
} You can then declare a method that takes this type as a parameter in several different ways: void SetSize(Size size); // C/C++: void SetSize(Size size)
void SetSize(ref Size size); // C/C++: void SetSize(Size* size);
void SetSize(ref readonly Size size); // C/C++: void SetSize(const Size* size); The exact semantics of each depends on the underlying calling convention.
|
Beta Was this translation helpful? Give feedback.
-
I know the usage and think it's good, I like it, but I don't think this will really be used much to worth being feature Still if this feature would develop I think we need to bring back C style constructor Such as fixed struct Vector3
{
public float X,Y,Z;
}
Vector3 position(0,0,0); |
Beta Was this translation helpful? Give feedback.
-
@Thaina How is that any better than this? struct Vector3
{
public float X, Y, Z;
}
var position = new Vector3 { X = 0, Y = 0, Z = 0 }; With Records (#39) and target-typed new expressions (#100) this will become even better: struct Vector3(float X, float Y, float Z);
Vector3 position = new(0, 0, 0); |
Beta Was this translation helpful? Give feedback.
-
I've worked with C style constructors a number of times and I hate them. There's no way (besides non-compiler-checked comments) to notate which field each value goes to. |
Beta Was this translation helpful? Give feedback.
-
@Happypig375 I mean if this 859 "Non-copyable structs" feature will be implemented. You cannot write var position = new Vector3 { X = 0, Y = 0, Z = 0 }; Because that was copying I just point out the fact that "Non-copyable structs" cannot call constructor by any of current C# syntax |
Beta Was this translation helpful? Give feedback.
-
In some case, mutable structs are very useful for obtaining high performance. For example:
However, mutable structs are evil for now. Non-copyability of mutable structs are the most feasible solution for this evil issue. |
Beta Was this translation helpful? Give feedback.
-
Rust's memory ownership model solves such a problem. static void Main()
{
var p1 = new Vector3 { X = 0, Y = 0, Z = 0 }; // ownership is transferred from LHS to p1
Write(p1); // OK. pass by reference
var p2 = p1; // transfer ownership again
Write(p2); // OK. pass by reference
Write(p1); // NG. p1 no longer has ownership
}
static void Write(in Vector3 p)
{
Console.WriteLine($"({p.X}, {p.Y}, {p.Z})");
} |
Beta Was this translation helpful? Give feedback.
-
see also: |
Beta Was this translation helpful? Give feedback.
-
I've created an analyzer for non-copyable structs: https://github.com/ufcpp/NonCopyableAnalyzer |
Beta Was this translation helpful? Give feedback.
-
@tannergooding mutable An example would be a version of public ref struct StackLazy<T>
{
private readonly Func<T> _func;
public T ValueOrDefault { get; private set; }
public bool IsCreated { get; private set; }
public StackLazy(Func<T> func) : this() => _func = func;
public T Value
{
get
{
if (!IsCreated)
{
ValueOrDefault = _func();
IsCreated = true;
}
return ValueOrDefault;
}
}
} This will work within the current frame, or if you pass by Other examples might be scope-based utilities for nestable operations such as logging entry/exit, timing, etc. The C++ equivalent would be a deleted copy ctor and assignment operator. |
Beta Was this translation helpful? Give feedback.
-
I like C++ RAII. I think a C# version would be a non-copyable struct that has a destructor(/Dispose method). Maybe because non-copyable structs require passing by ref, that should be the default behavior: the ref keyword is implicit (because the struct is 'refonly'). |
Beta Was this translation helpful? Give feedback.
-
Another example, this time not stack-constrained. The issue for such types is the method of assigning fields initially, in the absence of something like C++ constructor initializer lists). public nocopy struct DoubleCheckedLazy<T> where T : class
{
private readonly Func<T> _func;
private readonly object _lock;
private T _value;
public DoubleCheckedLazy(Func<T> func, object lock = default)
{
_func = func;
_lock = lock ?? new object();
}
public T Value
{
get
{
if (_value == null)
{
lock (_lock)
{
if (_value == null)
{
_value = func();
}
}
}
return _value;
}
}
} Usage in a type might then be: public class Foo
{
private readonly DoubleCheckedLazy<Bar> _bar;
public Foo()
{
// this would have to not be considered a copy
_bar = new DoubleCheckedLazy<Bar>(() => ExpensiveBarConstruction());
}
public Bar Bar => _bar.Value;
} This can be done safely today if I suspect that if we had It's probably simpler to allow |
Beta Was this translation helpful? Give feedback.
-
Are "non-copyable structs" actually coming anytime soon? I don't see a champion for them. The poster use case has some uses but the fact you can really turn the |
Beta Was this translation helpful? Give feedback.
-
As long as there's no Champion on the language team, the answer is pretty much guaranteed to be "No". |
Beta Was this translation helpful? Give feedback.
-
This should be considered also for non-copyable C# structs. |
Beta Was this translation helpful? Give feedback.
-
This proposal can be achieved with my proposal with RAII and delete copy constructor ... RAII C#: #4808 Example how it could be implemented: public struct Handle
{
public Handle()
{
...
}
...
// Delete copy constructor
public Handle(in Handle handle) = delete;
...
} |
Beta Was this translation helpful? Give feedback.
-
After initially having partially advocated against more advanced value types language support, I suddenly changed my mind in the middle of some advanced native interop scenarios. The problem is that one would need all the C++ features "pack" at once: non-copyable structs, destructors and move semantics, maybe others. While I feel/hope this could be done in a compact C# way, the fear that attempting to introduce such features turns into mimicking the huge syntax complexity of C++ is high. |
Beta Was this translation helpful? Give feedback.
-
This is such a niche problem, I'm not sure having a built-in solution is a good idea. Also, with modern C# tooling, an analyzer could be written to enforce usage of If you're lazy, .NET 9's new stack-allocated objects could help with performance of a |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
There are cases where one is interested in having objects with few data members and a small amount of code to deal with them, but want to avoid copying.
A real use-case we had is a lock-free flag: we had a lot of places in the code base that used Interlock functions to basically set a bool to 0 or 1, or used Volatile in completely wrong ways. We created a struct that looks something like this:
With a few inlined methods and some other things, in an attempt to make logic this less error-prone.
If you passed this struct as an argument to a function, a copy of the internal
_state
would ensue, and hence you would be keeping two separate flags. On the other hand, declaring this as a class adds the unwanted semantic of a shared flag, as well as unnecessary overhead for something that should be inlined and completely removed (although as a class it would be easy to prevent copying from happening).In C++, this would be easily fixed by explicitly deleting the copy constructor, copy assignment, move constructor, and move assignment operators. It would be great to be able to specify a "this should never be copied" constraint for the struct in C#.
Beta Was this translation helpful? Give feedback.
All reactions