c# record - using with keyword to modify properties - c#

I recently ran into the record keyword in c#, I'm able to create a an instance of a record and modify it both by assigning a value regularly and both by using the keyword with.
Is there any difference between the two ways, when should one use with?
public record Car{
public Car(string name, int age){
Name = name;
Age = age;
}
public string Name;
public int Age;
}
public static void Main()
{
var car = new Car("Reno", 15);
car.Name = "Honda";
Console.WriteLine(car.Name);
car = car with {Name = "BMW"};
Console.WriteLine(car.Name);
}

One of the main reasons for records introduction in C# - make it easier to create immutable data models. with functionality was created to provide easy to use syntax to create a copy of immutable instance with changed properties. So car = car with {Name = "BMW"}; actually does not modify original reference type but creates a new one and assigns it to the variable.
The difference can easily be seen with next code:
var car = new Car("Reno", 15);
var car2 = car;
car.Name = "Honda";
Console.WriteLine(car.Name);
Console.WriteLine(car2.Name);
car = car with {Name = "BMW"};
Console.WriteLine(car.Name);
Console.WriteLine(car2.Name);
Also couple of notes:
it is recommended to use autoproperties instead of fields, i.e.:
public record Car{
public Car(string name, int age){
Name = name;
Age = age;
}
public string Name { get; set; };
public int Age { get; set; };
}
in case you need immutable data records provide neat syntax which automatically generates constructor and init-only properties:
public record Car(string Name, int Age);

They are not the same. The difference is that with creates a copy of the record. According to the proposal:
A with expression allows for "non-destructive mutation", designed to produce a copy of the receiver expression with modifications in assignments in the member_initializer_list.
[...]
First, receiver's "clone" method (specified above) is invoked and its result is converted to the receiver's type. Then, each member_initializer is processed the same way as an assignment to a field or property access of the result of the conversion.
So there is an extra step of copying the record if you use with.
See also the decompiled C# code on SharpLab. Notice the <Clone> call before setting car2.Name = "BMW", which does not happen when setting car.Name = "Honda".
Car car = new Car("Reno", 15);
car.Name = "Honda";
Console.WriteLine(car.Name);
Car car2 = car.<Clone>$();
car2.Name = "BMW";
Console.WriteLine(car2.Name);

Related

how to compare values of two reference type nested and dynamic objects

I have two objects with the same type and values
how can I compare them by value?
exp:
class Person
{
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public Address Address { get; set; }
}
class Address
{
public string City { get; set; }
public int ZipCode { get; set; }
}
var p1 = new Person()
{
Name = "John doe",
BirthDate = new DateTime(2000, 1, 1),
Address = new Address(){
City = "some city",
ZipCode = 123456
}
};
var p2 = new Person()
{
Name = "John doe",
BirthDate = new DateTime(2000, 1, 1),
Address = new Address(){
City = "some city",
ZipCode = 123456
}
};
so how can I compare these objects with value?
Mabey in future I wanna change my objects so I need a general way that not depends on object properties names and types
use json
Convert each object into a json string, with all properties/fields sorted by name, then compare two strings. this is slow, but it works.
use reflection
Using reflection methods, compare each property/field one by one. Actually the json library do the same job. Doing the comparison yourself will save the time converting to string, but you have to dive into the nested types.
use some code generator, e.g. protobuf
If the datatype is just POCO type, maybe protobuf is a good choice. It introduces a lot advantages:
build-in comparison
json serialization and deserialization
very fast binary serialization and deserialization
cross-platform and cross language, integrated well with grpc inter-process communication
version compatibility, when new fields added, old data on disk can still be read by app.
just make an "Equal function" in your Person class
public bool class Equals(Person source)
{
if(this.Name!=source.Name) return false;
if(this.Surname!=source.Surname)return false;
....
return true;
}
Then use like that :
if(myFirstPerson.Equals(mySecondPerson))
{
}
Like that you can place as many as attributes as you want, then even make several Equals functions, if you need not to always compare the same attributes. Personally I always use this way (instead of "if values equal", I put "if value not equal then return false"), cause very useful when you have a lot of values to compare, and different equal functions.

When would you create an Object from an inherited class?

I'm currently looking into inheritance and polymorphism and I'm a bit confused about where you'd want to create a Person object of type Student?
assuming the following code:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
class Student : Person
{
public int YearOfStudy { get; set; }
public string Course { get; set; }
public string PredictedGrade { get; set; }
}
Now looking online, there are a few options here in terms of creating an object:
Person p = new Person();
Student s = new Student();
Person ps = new Student();
The first objects allows me to set name, age and gender, while the second allows me to set those 3, as well as yearsOfStudy, course and predictedGrade. But I'm unsure of what the third object allows me to do? I can still set all 6 parameters, however I can only use the attributes set in the Person class? Any explanation on the correct usage of the third object would be appreciated.
Thanks
Don't think of this as Person ps = new Student() yet.
The real benefit is being able to abstract common code for all types of Person. So your methods may take in a Person because that's all it needs and will work with any person type you create such as Janitor, Teacher, etc.
var myStudent = new Student()
VerifyAge(myStudent);
VerifyYearOfStudy(myStudent);
public bool VerifyAge(Person person)
{
return person.Age < 200;
}
public bool VerifyYearOfStudy(Student student)
{
return student.YearOfStudy <= DateTime.Now.Year;
}
To clear up some confusion the only time you ever really declare the base in a method is when you want to actually denote that this variable is only meant to be used as that specific type. Think of it as if you had declared your variable using an interface instead. Sure I am working with a Student instance, but I am only working with it as a Person instance or as IPerson.
Normally as a variable in a method you wouldn't do that because pretty much the defacto standard is to just use var for everything nowadays. Where you do make the choice to define Person is normally on properties, method return values, and method parameters. Local variable is not really important.
Because Student class is derived from Person class, any Student object is also a Person object. Thus a notation Person ps = new Student(); means we're declaring variable ps to be of type Person and instantiate it as Student. It could be used if you have a method that takes Person object as parameter, e.g.
public void Foo(Person p) { if(p.Age > 21) Console.WriteLine("OK to drink!"); }
However, if you have a method that operates on properties of derived class you must declare and instantiate the instance of it. So for
public void Foo(Student s) {if(s.YearOfStudy == 1) Console.WriteLine("Freshman"); }
you must use Student s = new Student();.

Immutable types with object initializer syntax

For example, I have an immutable type
class Contact
{
// Read-only properties.
public string Name { get; }
public string Address { get; }
}
And I hope I can use object initializer syntax to create a Contact
Contact a = new Contact { Name = "John", Address = "23 Tennis RD" };
But I cannot. Any possible way to make use of the powerful object initializer syntax in this case?
The closest thing would be a constructor with optional parameters:
class Contact
{
public string Name { get; }
public string Address { get; }
public Contact(string name = null, string address = null) {
Name = name;
Address = address;
}
}
Then you can call it with parameter names:
new Contact(
name: "John",
address: "23 Tennis RD"
)
The syntax is slightly different from an object initializer, but it's just as readable; and IMO, the difference is a good thing, because constructor parameters tend to suggest immutable properties. And you can specify the parameters in any order, or leave some out, so it's just as powerful as object initializer syntax.
This does require some extra code (defining the constructor, assigning all the properties), so it's more work than object initializer syntax. But not too terrible, and the value of immutable objects is worth it.
(For what it's worth, C# 7 may get immutable "record types" that have much simpler syntax. These may or may not make it into the final release, but they sound pretty cool.)
This is dated now, but with the release of C# 9 you can use init to achieve the desired functionality.
So your example would become:
class Contract
{
// Read-only properties.
public string Name { get; init; }
public string Address { get; init; }
}
And then you could initialize with:
// success!
Contract a = new Contract { Name = "John", Address = "23 Tennis RD" };
But you would still be unable to modify the parameters after setting them (so effectively they are still readonly).
// error!
a.Name = "Uncle Bob";
Under the hood, when you use object initializer syntax prior to C# 9 the compiler would call the default constructor first, and then set the property values you've specified. Obviously if those properties are readonly (i.e. only a get method), it can't set them. The init only setter allows setting the value only on initialization, either via a constructor method or object initializer syntax.
More info is available here: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#init-only-setters
Nope, you cannot use it with readonly properties.
Here are the different property and field types in comparism.
public class sometype {
public int readonlyProp{
get;
}
public int normalProp {
get;
set;
}
public const int constField = 3;
public readonly int readonlyField = 3;
public int normalField = 3;
public void test() {
sometype test = new sometype() { readonlyProp = 3}; // Doesn't work -> Property or indexer is readonly
sometype test1 = new sometype() { normalProp = 3 }; // ok
sometype test2 = new sometype() { constField = 3 }; // Doesn't work -> Static field or property
sometype test3 = new sometype() { readonlyField = 3 }; // Doesn't work -> readonly field
sometype test4 = new sometype() { normalField = 3 }; // ok
}
}
It is important to understand that const fields are considered static and thus are not instance members. And since the object initializer is used for instance members this doesn't work.
Object initializer will first construct the object, then set property values.
It needs setters.
It's short hand for:
Contact a = new Contact();
a.Name = "John";
a.Address = "23 Tennis RD";
A readonly field can't have it's values set once the object has been constructed. To have that class immutable, you'll need to create a constructor to take those values:
class Contact // Immutable class
{
// Read-only properties.
public string Name { get; }
public string Address { get; }
public Contact(string name, string address)
{
this.Name = name;
this.Address = address;
}
}

How can I extend a field of a class?

Suppose I have a List of Person (which is a class). It contains about 20 field (Name, Surname, Age, DateOfBirthdate, and so on). So I got this list:
var listOfPersons= MyContext.Persons.Cast<Person>();
Now, I need to iterate through this List, and for each Person adding a new field (which it is not present in the class), called, let's say, CurrentDateTime.
I could create a new object, with the new field, and "copy & paste" values from Person to the new Class. Somethings like:
PersonNew newPerson = new PersonNew("Name", "Surname", "Age", "DateOfBirthdate", ... "CurrentDateTime");
But this is very bad if in the future I change the Person class. So, is there a strategy to "extending Person" with a new field? That takes the Person instance (whatever it is) and adds the new field?
You can create some static method that create PersonNew from Person using Automapper.
public class PersonNew : Person
{
public static PersonNew CreateFromPerson(Person person, DateTime currentDateTime)
{
var newPerson = Mapper.Map<PersonNew>(person);
newPerson.CurrentDateTime = currentDateTime;
}
}
I think that the solution you described works fine. If you want to keep track of each person's birthday without extending the Person class, you might use a Dictionary object
var listOfPersons = MyContext.Perons.Cast<Person>();
Dictionary<Person, DateTime> birthdays = new Dictionary<Person, DateTime>
foreach(Person person in listOfPersons)
{
birthdays.Add(person, getBirthday(person);
}
One solution is to make your class partial, and add your field in another partial definition of your class:
public partial class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
...
}
...
public partial class Person
{
public DateTime CurrentDateTime { get; set; }
}
...
var listOfPersons = MyContext.Persons.Cast<Person>();
foreach (var person in listOfPersons)
{
person.CurrentDateTime = ....
}
Do note that you will use the same instance of your class.
First I would suggest using extension methods for projecting collections instead of iterating. Like that:
var newCollection = oldCollection.Select(entity => MakeNewType(entity))
Second, it's not completely clear what you mean by "extending Person" with a new field. Here are the couple of ways you can accomplish that.
1) Make another class with the new field and map it to the old one. This is a common scenario for asp.net mvc application where you map models to the appropriate viewmodels. Automapper is useful for these types of scenario (see SÅ‚awomir Rosiek anwser)
2) Take advantage of dlr in c# 4+. Yuo will lose the intellisense for dynamic objects, but they canned be passed around functions
var newPeople = people.Select(p =>
{
dynamic expando = new ExpandoObject();
expando.Id = p.Id;
expando.FirtName = p.FirtName;
/* ... */
expando.CurrentDateTime = DateTime.Now;
return expando;
});
3) Use Anonymous types. Anonymous types cannot be passed to another functions, so this approach is useful when you need to quickly project data inside a single method and calculate some result
var newPeople = people.Select(p => new
{
Id = p.Id,
FirtName = p.FirtName,
/* ... */
CurrentDateTime = DateTime.Now
});
in both cases you can now access newly "created" property:
foreach(var p in newPeople)
{
Console.WriteLine("CurrentDateTime: {0}", p.CurrentDateTime);
}
4) If you really need to create a fully featured .net class at runtime you can use Reflection.Emit. This scenario is typically used to create dynamic proxies - subclasses which implement some functionality only known at runtime. Entity framework does this.

.NET/C#: Is it possible to replace one object in memory with another?

Take this code for example:
class Jooky
{
static long Last;
public Jooky() { Id += Last++; }
public long Id { get; set; }
public string Name { get; set; }
}
class Flooky
{
public Flooky() { Jooky1 = new Jooky(); Jooky2 = new Jooky(); }
public Jooky Jooky1 { get; set; }
public Jooky Jooky2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Flooky> Flookies = new List<Flooky>();
//I build a collection of flookies to emulate the service call of
//FlookyProxy.GetAllFlookies().
for (int i = 0; i < 5; i++) Flookies.Add(new Flooky());
//This makes a collection of all the jookies in all the flookies.
var Jookies = Flookies.Select(f => f.Jooky1).Union(Flookies.Select(f => f.Jooky2));
//I get the jooky.
Jooky Jooky = Jookies.Single(j => j.Id == 2);
//Fig 1: I just got a jooky out of the collection. One of the flookies
//has a reference to this jooky. I want to set the jooky to a new
//reference, but still want the same flooky to reference it.
Jooky = new Jooky { Name = "Bob" };
//I get the jooky again
Jooky = Jookies.Single(j => j.Id == 2);
//However, this writes an empty string because only the Jooky variable
//I previously declared was affected.
Console.WriteLine(Jookies.Single(j => j.Id == 2).Name);
//Basically, I want the code in Fig 1 above to be the same as:
//Flooy.Jooky = new Jooky { Name = "Bob" };
Console.Read();
}
}
Basically, variable A is referencing Aa in memory and variable B is referencing object Bb in memory. I want to make A reference the same object in memory as B without going like A = B;. Instead, I want to replace the physical object in memory with another, ultimately going like Aa = Bb;.
Is this at all possible?
Update: Primary rule: I cannot reference the flooky directly, so I can't be all like Flooky.Jooky1 = new Jooky() or Flookies[3].Jooky1 = new Jooky().
Maybe this is possible with unsafe code as suggested by havardhu, but it's definitely not possible with safe code. It's important to understand why doing what you're trying to do is unsafe. Not only does it break encapsulation, it breaks type safety. Consider this example.
class Me : IHaveCar
{
BuickCentury myCentury = new BuickCentury(2004);
public Car Car { get { return myCentury; } }
public void Drive()
{
myCentury.CruiseWithAuthority();
}
}
class EvilOilChangeService
{
public void ChangeOil(IHaveCar customer)
{
Car car = customer.Car;
// here's the fictional "replace object in memory" operator
car <<== new VolkswagenBeetle(2003);
}
}
The EvilOilChangeService can create a situation where myCentury is referencing a VolkswagenBeetle! I'm going to be in trouble when I try to go for a Drive because a VolkswagenBeetle just can't CruiseWithAuthority like a BuickCentury can (especially when the driver is 6'2")
Even in C/C++ which allows willy-nilly memory access, I would still be quite surprised by code that does what you want to do. This is why most of the other answers are suggesting a different approach or design.
Change:
//Jooky = new Jooky { Name = "Bob" };
Jooky.Name = "Bob" ;
The resullt of the .Single() is a reference to an instance (object). You were just overwriting the reference with one to a new object. The old object was not changed or overwritten.
To understand what's going on, and to adjust what you are aiming for, look up "Value Type and Reference Type". Lots of reading to do.
After reading the comment:
If your Details (Jookies) are going to change independently of their Owners (the Flookies) then you just need another layer of indirection.
A simple suggestion:
do not store references to the details (since they will change)
store a DetailId instead (JookyId1, JookyId2)
keep the Details in a Dictionary (Dictionary<int,Jooky>)
create a (readonly) property in Owner to get Detail1 by looking it up in the dictionary.
You can write unsafe code in C# which enables you to operate on direct memory.
Have a look here for details:
Pointers and arrays in C#
You'll notice that you can use the familiar pointers (*) and addresses (&) from C and C++.
Here's an example of an unsafe swap, which I think is what you're after:
Unsafe swap in C#
Jooky = new Jooky { Name = "Bob" };
Flookies[0].Jooky1=Jooky;
If you want to replace and object with another without just assigning references you just to copy all the data fields to the other object. Not sure if i have understood your question correctly.
When you're working with references, every assignment to a reference changes the object that reference points to.
So, when you say:
Jooky Jooky = Jookies.Single(j => j.Id == 2);
you're creating a reference to the Jookie with Id == 2. And then, when you say Jooky = new Jooky { Name = "Bob" };, you're telling that reference you created to point to the Jooky you have just created instead.
So, if you want to set a new value to the Jookie1 property (wich is a placeholder for a reference to a Jookie object) of the Flookies[0] object, you got to say:
Flookies[0].Jooky1 = new Jooky { Name = "Bob" }; (as stated by #Ashley John's answer).
That way, you're telling the Flookies[0].Jooky1 reference to point to the new Jooky { Name = "Bob" }; object.
For further explanation, see http://msdn.microsoft.com/en-us/library/ms173104.aspx .
If you have access to the Jookie class, you could add a property that holds the parent Flookie of the Jookie:
class Jooky
{
static long Last;
public Jooky(Flooky parent)
{
Id += Last++;
Parent = parent;
}
public long Id { get; set; }
public string Name { get; set; }
public Flooky Parent { get; private set; }
}
and then access the parent Flookie and change it's Jookie1 property:
Flookie flookie = Jookies.Single(j => j.Id == 2).Parent;
flookie.Jookie1 = new Jookie { Name = "Bob" }

Categories