29 ms·
> Immutable data is also becoming a common pattern even in C# but it's tedious as hell to write. I can simplify it for you: public class TestClass : Recor
by louthy 9y ago
> Immutable data is also becoming a common pattern even in C# but it's tedious as hell to write.
I can simplify it for you:
public class TestClass : Record<TestClass>
{
public readonly int X;
public readonly string Y;
public readonly Guid Z;
public TestClass(int x, string y, Guid z)
{
X = x;
Y = y;
Z = z;
}
}
That is an immutable type that has structural equality and ordering, a strong GetHashCode() implementation, as well as a default ToString() which previews the members, and serialization and deserialization constructs. It's a feature I recently added to language-ext [1].
If you need `With` functionality, then add the following member:
public TestClass With(int? X = null, string Y = null, Guid? Z = null) =>
new TestClass(X ?? this.X, Y ?? this.Y, Z ?? this.Z);
Then you can use named parameters to do partial updates:
value = value.With(X: 456, Y: "World");
Obviously it's still more boilerplate than F#, but C# is never going to have significantly less until we get proper record types and ADTs.
> Functional programming is also seeping in but again tons of boiler plate.
Do you have an example? It's something I've worked on quite heavily over the past few years, so I'm always looking for areas to try and improve the lot of C# devs trying to write functionally.
[1] https://github.com/louthy/language-ext/releases/tag/v2.1.0 https://github.com/louthy/language-ext/releases/tag/v2.1.0
- jdmichal 9y agoIn C# 6.0 you can also make them properties instead of fields, though I have no idea if this works with your "Record" class. public class TestClass { public int X { get; } public string Y { get; } public Guid Z { get; } public TestClass(int x, string y, Guid z) { X = x; Y = y; Z = z; } } And C# 7.0 shortens this to: public class TestClass( int X, string Y, Guid Z);
- louthy 9y ago> In C# 6.0 you can also make them properties instead of fields Why would I want to? It's more boilerplate for no gain. I have never understood the obsession with properties over fields. I realise that to some serialisation or interop libraries that rely on reflection to access properties it's important, but to my mind, those solutions should also support public fields. And obviously inheritance-land with interfaces, but I find writing in a functional style that interfaces tend to be a rarity rather than the default setting. > though I have no idea if this works with your "Record" class. Yes, field backed properties are supported in the structural equality and ordering. > And C# 7.0 shortens this to... It doesn't. That maybe coming in a later release, but neither C# 7.0 nor 7.1 support the record syntax.
- tjalfi 9y agoC# 7 does allow one to shorten that class a bit further. I saw this trick in the MEAP[0] of C# in Depth. public class TestClass { public int X { get; } public string Y { get; } public Guid Z { get; } public TestClass(int x, string y, Guid z) => (X, Y, Z) = (x, y, z); } Disclaimer: I don't combine expression-bodied constructors and tuple construction/deconstruction in my code. I will use either feature but the combination seems likely to annoy my coworkers. I also haven't measured the costs of this idiom. [0] https://www.manning.com/books/c-sharp-in-depth-fourth-edition https://www.manning.com/books/c-sharp-in-depth-fourth-editio...
- pjmlp 9y ago> I have never understood the obsession with properties over fields. Encapsulation, allowing to change the internal representation without requiring clients to update or recompile their code. Ensuring the class invariants are always kept valid.
- naasking 9y ago> Encapsulation, allowing to change the internal representation without requiring clients to update or recompile their code This benefit is exaggerated. In practice, classes end up being either record-like where you need visibility on all the actual fields, or they are protocol-like/more fully encapsulated with few public members and more methods. The addition of properties was a big mistake IMO. Read/write fields should just be part of interface specifications. There is little value beyond that.
- pjmlp 9y ago> The addition of properties was a big mistake IMO. Properties go all the way back to Smalltalk, which doesn't expose class internal data. > Read/write fields should just be part of interface specifications. There is little value beyond that. No because you cannot ensure their invariants, specially if more than one field needs to change at the same time.
- rubber_duck 9y ago>If you need `With` functionality, then add the following member: Without it it's useless - and do that example when fields aren't named X, Y, Z but have actual descriptive property names + 5-10 properties like DB tables. It gets ridiculous really fast. >Do you have an example? Record types, result piping (best you can do is extension methods - again boilerplate), pattern matching, etc. I recently wrote a C# report integration service - it was a C# service that connected to a DB at specific time intervals, did a bunch of data transformations then passed it off to different services over file system or TCP. Wrote it functional style with C# (immutable collections, record types, static functions). I then rewrote the same project in F# because I wanted how much simpler the code would be in practice - off the top of my head I managed to reduce a ~20k LOC project filled with boilerplate to ~6k LOC of pure business logic data transformation and protocol specific data formatting. IIRC I had 5 logically grouped source files in F# that had connected logic inside of them (ie. record types were "one liners" defined in the same file they were used not a separate file like the C# tediousness) The resulting code was easier to read because there was just less crap, easier to test from REPL/debug isolated issues and unit test. C# is not Java but it's still ridiculously verbose for high level programming.