To make code like this work in C++ you typically must provide a copy constructor for the Widget class. Copying objects in C++ is pretty common, it is often required if you store objects in a collection. It is however a source of horrid heap corruption bugs, the default copy constructor implemented by the compiler isn't always appropriate. It aliases pointers, copying the pointer value instead of the pointed-to value. A shallow copy instead of a deep copy. That blows up badly when the destructor of the class deletes the pointed-to object, like it should, leaving the pointer in the copy pointing to garbage.
The C# language doesn't have this behavior, it does not implement a copy constructor. It is very rarely required anyway, the garbage collector avoids the need to make copies. Very notable in any benchmark on collection classes.
You certainly can create your own copy constructor, you just have to implement it explicitly:
void Widget(Widget other) {
this.X = other.X;
// etc...
}
And create the copy explicitly as well:
Widget w = new Widget();
w.X = 42;
// etc...
foo(new Widget(w));
Which perhaps also makes it clearer that there's a non-zero cost to making those copies. If you want a widget to have value behavior automatically then make it a struct, not a class. When passed without the "ref" keyboard, the runtime now automatically makes the copy. It does a memberwise copy. It is shallow, just like the default C++ copy constructor. But without the pointer aliasing problem, the garbage collector solves that.
But with the shallow copy problem. Which most C# programmers tackle with "I tried that before, didn't work out well. Let's not do that again". Good advice, you shouldn't do that in C++ code either. It's expensive and error prone and you better know what you're doing. Maybe that's trivializing the problem a bit too much. Sorry.