I stumbled upon this article, which is devoted to chips, which with high probability will be added to the new version of the language.
Stumbled there on Records .
Little is not clear why we need a castrated class.
I stumbled upon this article, which is devoted to chips, which with high probability will be added to the new version of the language.
Stumbled there on Records .
Little is not clear why we need a castrated class.
With each new version of C #, more and more chips from functional programming are added to it, and Records is one of those chips. From the functional programming paradigm :
The main feature of functional programming, which determines both the advantages and disadvantages of this paradigm, is that it implements a stateless computation model. If an imperative program at any stage of execution has a state, that is, a set of values for all variables, and produces side effects, then a purely functional program has neither the state nor parts of it, nor does it produce any side effects. What is done in imperative languages by assigning values to variables is achieved in functional languages by passing expressions to function parameters. The immediate consequence is that a purely functional program cannot change the data it already has, but can only generate new ones by copying and / or expanding old ones.
Records is simply an immutable class that cannot be changed. In some cases, this approach reduces the number of possible errors. Take a simple code:
MyClass something = new MyClass(arg1, arg2, arg3); MyMethod(something); What happened in MyMethod ? Nobody knows. Has the state of something changed? Is it in a valid state or do you need to immediately check something after throwing the method and throw in Exception ?
The most famous and frequently used class in C # is String . I think everyone will agree that making a mistake when working with strings is very difficult. This class only benefited from immunity. Of course, this is not a very good analogy with Records, because the lines have their own methods, but if you really need, we can add extension methods for Records. Despite the fact that it is not very convenient, all the same, the code will end up less than if you manually import the immiable class.
This is syntactic sugar for a shorter class declaration that simply contains a set of fields.
The example from the article shows that the following class:
public class Sword : IEquatable<Sword> { public int Damage { get; } public int Durability { get; } public Sword(int Damage, int Durability) { this.Damage = Damage; this.Durability = Durability; } public bool Equals(Sword other) { return Equals(Damage, other.Damage) && Equals(Durability, other.Durability); } public override bool Equals(object other) { return (other as Sword)?.Equals(this) == true; } public override int GetHashCode() { return (Damage.GetHashCode() * 17 + Durability.GetHashCode()); } public void Deconstruct(out int Damage, out int Durability) { Damage = this.Damage; Durability = this.Durability; } public Sword With(int Damage = this.Damage, int Durability = this.Durability) => new Sword(Damage, Durability); } You can declare like this:
public class Sword(int Damage, int Durability); And the compiler himself thinks out the rest: constructors, properties, comparison.
If you need a class with non-default functionality, you can always use the full syntax of class declaration. This innovation will save code in cases where you need a class with a set of fields and nothing more.
Saving code is indicated as the main rationale in a sentence on Github :
Motivation
There are few more than aggregate collections of typed data. Unfortunately, declaring such types requires a great deal of boilerplate code. Records provide for a mechanism for declaring a datatype by reciting, if any.Justification
Much of the declared types in C # differ slightly from the typed data set. Unfortunately, the declaration of these types requires a large number of template code. Records provide a mechanism for declaring a data type by listing the members of a set or differences from the standard template, if any.
public class Sword : IEquatable<Sword> { куча строчек } ? - Andrei NOPThe record class is not an "castrated class", but an immutable class, in which some of the important methods are already implemented by the compiler. The word "immutable" here is not a flaw, but an important feature.
Such classes are simply necessary for the following tasks:
use as a key in the dictionary: every time you need a dictionary by two parameters, you have to either use tuples, or use anonymous types, or write your class with the Equals and GetHashCode methods, and use tuples in the public API — bad form, and anonymous types lead to an untyped key;
data caching: the data in the cache must be immutable, which can be achieved either by defensive copying - or by immobility;
multi-threaded access: Immotable data can be accessed from any streams without blocking.
Below is a pseudo code in which many details are omitted, including access specifiers and other husks that are not important to understand. It also uses the minimum number of fields to reduce the code.
Imagine that you have such a class:
class Person { string name; } And this is the class for storing the previous instances:
class CompanyRegister { List<Person> persons; } CompanyRegister is the main repository of all persons in the program, so if someone needs to add a person, or get data from it, he refers to this class. To get a person on some basis, we need the GetPerson() function, but what will it return? Let her return Person , is this a problem? Of course. If we return Person then any entity can change this object outside the CompanyRegister !
But this contradicts the fundamentals of our class: only he is responsible for the changes (he can notify other entities, or he can be engaged in some kind of tracking of changes - it doesn’t matter). How do we solve the problem? If we wrote in C ++, we would make a return type const Person& , and the problem would be solved. But we have C #, which, for all its merits, cannot boast of having tools such as C ++ in the part of “stopping” and preventing unnecessary copies.
Speaking of copies, we could have made Person structure, but that would entail unnecessary copying, which everybody tries to avoid as much as possible. Based on the fact that no one is going to turn the language into C ++, and in C # [almost] everything is a link, the developers turned their attention towards functional programming and adopted unchangeable types. The implementation of such types in C # is a bunch of code that you need to write for each class, or use all sorts of code generators. Here's how, for example, we could make our Person unchangeable:
class Person { string name; Person(string name) { name = name; } Builder GetBuilder() { return new Builder(this); } class Builder { string name; Builder(Person person) { name = person.name; } Person ToImmutable() { return new Person(name: name); } } } It looks wonderful, isn't it? Now imagine that we have not one field, but about a dozen fields. How do you? So Records does the same thing, just do not write all this, and the syntax for creating new objects based on old ones becomes much more convenient.
How often is all this needed? Highly. After all, we very often have data for which someone is responsible, and this someone does not want them to be changed by all and sundry outside of this class. This is very common in GUI applications when data is transferred from a model to a view (VM and others).
const Person& make the type immutable? - tym32167 2:01const Person& makes the return value unchanged; more precisely, it returns a reference to an object that cannot be changed. Equivalence and hashcode are important, but not fundamental in the example that I gave. - ixSciSource: https://ru.stackoverflow.com/questions/903803/
All Articles