What is the "with" operator for in C#? - c#

I've came across this code:
var rectangle = new Rectangle(420, 69);
var newOne = rectangle with { Width = 420 }
I was wondering about with keyword in C# code. What is it for? And how can it be used? And what benefits does it bring to the language?

It's an operator used in expressions for easier duplication of an object, overriding some of it's public properties/fields (optional)
with expression - MSDN
Currently it can only be used with records. But maybe there will be no such restriction in the future (assumption).
Here's an example how it can be used:
// Declaring a record with a public property and a private field
record WithOperatorTest
{
private int _myPrivateField;
public int MyProperty { get; set; }
public void SetMyPrivateField(int a = 5)
{
_myPrivateField = a;
}
}
Now let's see how with operator can be used:
var firstInstance = new WithOperatorTest
{
MyProperty = 10
};
firstInstance.SetMyPrivateField(11);
var copiedInstance = firstInstance with { };
// now "copiedInstance" also has "MyProperty" set to 10 and "_myPrivateField" set to 11.
var thirdCopiedInstance = copiedInstance with { MyProperty = 100 };
// now "thirdCopiedInstance " also has "MyProperty" set to 100 and "_myPrivateField" set to 11.
thirdCopiedInstance.SetMyPrivateField(-1);
// now "thirdCopiedInstance " also has "MyProperty" set to 100 and "_myPrivateField" set to -1.
NOTE for reference types from MSDN:
In the case of a reference-type member, only the reference to a member instance is copied when an operand is copied. Both the copy and original operand have access to the same reference-type instance.
That logic can be modified by modifying the copy constructor of a record type. Quote from MSDN:
By default, the copy constructor is implicit, that is, compiler-generated. If you need to customize the record copy semantics, explicitly declare a copy constructor with the desired behavior.
protected WithOperatorTest(WithOperatorTest original)
{
// Logic to copy reference types with new reference
}
And in terms of what benefits it gives, I think it should be quite obvious now, that it makes copying of instances much easier and convenient.

Basically, the with operator will create a new object instance (records only, for now), by "coping values" from the "source" object and override some named properties in the destination object.
For example, instead of doing this:
var person = new Person("John", "Doe")
{
MiddleName = "Patrick"
};
var modifiedPerson = new Person(person.FirstName, person.LastName)
{
MiddleName = "William"
};
you can do this:
var modifiedPerson = person with
{
MiddleName = "Patrick"
};
Basically, you will write less code.
Use this source to get more details on the example above and official documentation for more examples.

Short answer is the following:
with keyword in C# was added for easier copy of complicated objects, with a possibility to override some of the public properties.
Examples are already briefly provided in the accepted answer.

Related

Pass an object Variable as a String to a Textbox in WPF/C#

I'm creating a practice application to practice using text files as a database.
Take a pipe delineated text file with 14 People Data Entries
Split the text file on the pipes and add each entry to a add each entry (about 150) to a list of the objects.
I've casted list of objects to a string.
Now I want a button click to display that list in a textbox.
Here is the code.
namespace StaffRosterLewis.ViewModel
{
class LoadData
{
public static void LoadRosterData()
{
string mypath = #"J:\zUtilities - Program Files\";
mypath += "StaffRoster - RosterOld.txt";
List<Model.Person> people = new List<Model.Person>();
List<string> lines = File.ReadAllLines(mypath, Encoding.ASCII).ToList();
foreach (var line in lines)
{
string[] entries = line.Split('|');
Model.Person newPerson = new Model.Person
{
LastName = entries[1],
FirstName = entries[2],
Extension = entries[3],
Department = entries[4],
Team = entries[5],
Group = entries[6],
Title = entries[7],
Shift = entries[8],
EmergencyResponder = entries[9],
AEDCPRCert = entries[10],
Languages = entries[11],
Notary = entries[12],
Note = entries[13],
DutyLocation = entries[14]
};
//newPerson.Unknown15 = entries[15];
people.Add(newPerson);
}
people.ToString();
}
}
}
I think the problem is here where the button click happens. I am simply trying to show the content of people above, but I lose the variable "people" no matter how I try to reference it. The above code is public and I made it static so the people variable should be accessible anywhere within the project. (I thought)
private void Button_Show_Click(object sender, RoutedEventArgs e)
{
Button_Show.Content = $"{LoadData.LoadRosterData.people.ToString()}";
}
There are many problems with your code.
You are trying to access a method like a static property here.
Second, you have return type a void, which should be of type string.
Third you should override the ToString method to return the list items as sting in the required format.
You should use Path.Combine to get the path.
If you are planning to make the people as static variable then you have to accept that it’s not thread safe and make sure you reset it whenever it’s necessary else you may face unexpected items in the list.
And your code will throw exception if you have less than 15 |in a line
You don't need to make LoadRosterData static. However, as mentioned by other commenters the method must return a result (in context of your current usage).
Because reading from a file can be resource consuming, you could increase performance when you store the result in a e.g. public property Peoples.
This allows to access the collection without forcing to read the probably unchanged file. Since this would mean to introduce an instance variable, it's recommended to make LoadRosterData an instance member too.
To create a string from the collection you can use the StringBuilder. The below example uses the StringWriter, which allows asynchronous string creation. StringWriter also uses a StringBuilder internally.
You can further improve the code by overriding ToString of the Person type.
To improve the file reading performance you should use the asynchronous API of the StreamReader instead of the synchronous File class. Using StreamReader.ReadLineAsync will also save you an extra loop.
To make file handling and data model creation in particular more convenient, you should consider to make use of serialization.
The recommended text format would be JSON. See Microsoft Docs: How to serialize and deserialize (marshal and unmarshal) JSON in .NET to learn how to do it. Deserializing the JSON file (preferably asynchronously) will automatically produce a collection of Person items without any hassle (this will eliminate the ugly and fragile index based access (to initialize instance properties) as well as making any delimiters like your pipe '|' redundant).
You are accessing the array starting from index '1'. But in computer science, index always starts from '0'. Not sure if you start from index '1' on purpose.
The following code fixes some issues and implements some performance improvements. It also shows how to convert the collection to a string, where each Person item is displayed in it's own line:
PersonDataReader.cs
class PersonDataReader
{
// Consider to use an ImmutableList to prevent modification.
// In the current context, this property could (and probably should) defined private.
public List<Person> Persons { get; }
// If Person is private, 'HasData' has to be defined private too
public bool HasData => this.Persons.Any();
// Constructor
public PersonDataReader() => this.Persons = new List<Person>();
public async Task<string> CreateRosterSummaryAsync()
{
// Use StringWriter to enable asynchronous string creation.
// StringWriter also uses a StringBuilder internally to improve performance.
using (var textWriter = new StringWriter())
{
if (!this.HasData)
{
await LoadRosterDataAsync();
}
// Alternatively use LINQ,
// for example Enuemrable.Select together with Enumerable.Aggregate
// to concatenate the Person.ToString values
foreach (Person person in this.Persons)
{
string personString = person.ToString();
// Write a Person per line
await textWriter.WriteLineAsync(personString);
}
return textWriter.ToString();
}
}
private async Task LoadRosterDataAsync()
{
this.Persons.Clear();
// Use Path.Combine to ensure a valid formatted path (improve robustness)
string sourcePath = Path.Combine(#"J:\zUtilities - Program Files", "StaffRoster - RosterOld.txt");
// Test if the file actually exists to avoid the expensive exception.
// If the file not found exception is desired, remove the File.Exists condition.
if (File.Exists(sourcePath))
{
return;
}
using (var fileReader = new StreamReaeder(sourcePath, Encoding.ASCII))
{
while (!fileReader.EndOfFile)
{
var line = await reader.ReadLineAsync();
string[] personValues = line.Split('|', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
// If you would use serialization, constructing the type explicitly
// can be avoided. A recommended text format to allow easy de-/serialization is the JSON format.
var newPerson = new Model.Person
{
// Check if starting from index '1' is really correct
LastName = personValues[1],
FirstName = personValues[2],
Extension = personValues[3],
Department = personValues[4],
Team = personValues[5],
Group = personValues[6],
Title = personValues[7],
Shift = personValues[8],
EmergencyResponder = personValues[9],
AEDCPRCert = personValues[10],
Languages = personValues[11],
Notary = personValues[12],
Note = personValues[13],
DutyLocation = personValues[14]
};
this.Persons.Add(newPerson);
}
}
}
}
Person.cs
class Person
{
/* Properties of Person */
// Example text representation of a 'People' object
// using string interpolation
public override string ToString()
=> $"Lastname: {this.LastName}; Firstname: {this.FirstName}; Duty location: {this.DutyLocation}";
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
private PersonDataReader PersonDataReader { get; }
public MainWindow()
{
InitializeComponent();
this.PersonDataReader = new PersonDataReader();
}
private async void Button_Show_Click(object sender, RoutedEventArgs e)
{
string personPerLineText = await this.PersonDataReader.CreateRosterSummaryAsync();
// Why did you chose a Button here? It should be
// this.DisplayTextBox.Text = personPerLineText;
this.Button_Show.Content = personPerLineText;
// Consider to display the complete List<Person> in a ListBox.
}
}
Remarks
A ListBox is probably the better choice to display the list of Person. It's more intuitive to implement and would eliminate the need to worry about string representations and creation.
Your method LoadRosterData has a return value void, means nothing. If you would like to return a string value and use it you may do it like this:
//returns a string
public static string MyStringMethod()
{
string str = "My string";
//Specify the return value with the return keyword
return str;
}
Or if you want to return a list:
public static List<string> MyListMethod()
{
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
return list;
}
You can't do ToString() with list, but you can refer to the index of a person.
And the useage:
button.Content = $"{MyStringMethod()}"
Or if you want to get person by name you can use IndexOf() method:
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
string number = "one";
button.Content = $"list[list.IndexOf(number)]";

setting an object reference to null

var list1 = new List<obj>();
var list2 = new List<obj>();
var item = new obj();
list1.Add(item);
list1.Add(item);
is there a way in c# to set all references in the current application to null if one reference that has the same reference value got set to null?
for example when i do list[0]=null or item=null, i want to set any reference in the current application that holds the same reference value to null so that the GC handles them, at the same time i dont want this behaviour for all Types, is there such thing in c#?
to get the idea
if i say
list1[0] = null
i want it at the same time to mean
list2[0] = null;
item = null;
There isn't a way to automatically set other variables to null, but if your point is to get the object garbage collected even though you still have other references to it, you can do that.
You need to use weak references. Weak references are documented here
You need to introduce an additional layer of indirection in your memory references. The simplest way would be to create a Nullable<T> type for classes:
public class Nullable<T> where T : class
{
public T Value { get; set; }
public Nullable(T initial)
{
this.Value = initial;
}
}
You could then use this type to wrap your objects:
var list1 = new List<Nullable<object>>();
var list2 = new List<Nullable<object>>();
var item = new Nullable<object>(new object());
list1.Add(item);
list2.Add(item);
item.Value = null; // clears value from all collections

Creating a grab-bag (collection) of original references to objects

I'm looking for a type/method of collection where I can add an object to a group of objects, then separately change the attributes of that object, and have those changes reflected in the object within the collection.
I've heard that List<T> adds values by reference, so I figured that the reference would be to the same object. In other words, I assumed:
List<string> valuesList = new List<string>();
string initialValue = "Alpha";
valuesList.Add(initialValue);
initialValue = "Bravo";
bool incorrectAssumption = (valuesList[0] == "Bravo");
I had hoped that 'valuesList' would then contain the new value, "Bravo." Tried it out and I realized that the List copies the reference, it doesn't absorb it, so valueList still only has the "Alpha" value. Are there any ways to use a collection as a legitimate handful of the objects they contain?
And in case it helps to see the actual business need....
List<BaseWidget> widgets = new List<BaseWidget>();
DerivedWidget specialWidget = new DerivedWidget();
DerivedWidget extraSpecialWidget = new DerivedWidget();
widgets.Add(specialWidget);
widgets.Add(extraSpecialWidget);
specialWidget.Run();
extraSpecialWidget.Run();
if (!widgets.Any(x => x.RunSuccessfully)) return false;
(Where the Run() method sets the RunSuccessfully property, which I'd like to have reflected in the 'widgets' list.)
============================================================================
UPDATE
As it's been pointed out in the answers and comments, there's a bit of a discrepancy between the business need mock-up and the dry-run example. I'll condense the life-lesson into this: it seems List<objects> have their changes tracked, whereas List<values> don't.
Well. It seems that you don't understand what happens really. Here is great article about .net type internals.
Shortly, what happens in your example with strings:
You create list
You create variable initialValue of string type. Value of this variable stores in special local variables container. Because string is reference type, in container of local variables it contained as a pointer to object.
You create new string "Alpha", storing it in heap, and assign pointer (to this string) to your local variable.
Then you are adding object to list. In your List this object stored as pointer to somewhere.
Then you are changing content of local variable 'initialValue' by assign it to pointer to another string. So, now in local variable 'initialValue' is one pointer, in list is another pointer.
Well, what about solutions?
Wrap your string to some another class. Like this:
class Wrapper<T> {
public T Content {get;set;}
public Wrapper(T content) {
Content = content;
}
}
Usage:
void Main()
{
var valuesList = new List<Wrapper<string>>();
var initialValue = new Wrapper<string>("Alpha");
valuesList.Add(initialValue);
initialValue.Content = "Bravo";
Console.WriteLine(valuesList[0].Content);
}
A bit ugly syntax.
Use clojures:
void Main()
{
List<Func<string>> valuesList = new List<Func<string>>();
string initialValue = "Alpha";
valuesList.Add(() => initialValue);
initialValue = "Bravo";
Console.WriteLine(valuesList[0]() == "Bravo");
}
All references to non-value types will be passed by reference, List<T> or not. String is a value type, however, and will always be passed by value. They are also immutable, so any time you change one you're actually creating a new String.
For your example, you could create a wrapper type to contain your string, and store this in your List<T>.
It seems that your actual business case should work properly, unless they are declared as structs.

How to edit value data in a dictionary C# [duplicate]

This question already has answers here:
How to update the value stored in Dictionary in C#?
(10 answers)
Closed 9 years ago.
I have a dictionary of members where the key is a unique long ID and the value is an object which contains data on that members name surname and other forms of member details. Is there any way in C# that this can be done?
e.g
dictionary key holds memberID 0 member
id 0 name is bob lives in Italy
bob moves to England
is there a way to update the dictionary in C# so that his entry now says he lives in England?
Assuming that Member (or whatever) is a class, it's simple:
members[0].Country = "England";
You're just updating the object which the dictionary has a reference to. Just to step through it, it's equivalent to:
Member member = members[0];
member.Country = "England";
There's only one object representing Bob, and it doesn't matter how you retrieve it.
In fact, if you already have access to the instance of Member via a different variable, you don't need to use the dictionary at all:
// Assume this will fetch a reference to the same object as is referred
// to by members[0]...
Member bob = GetBob();
bob.Country = "England";
Console.WriteLine(members[0].Country); // Prints England
If Member is actually a struct... well, then I'd suggest rethinking your design, and making it a class instead :)
For classes (at least, those that are mutable) this should be as simple as:
long theId = ...
yourDictionary[theId].Country = "England"; // fetch and mutate
For structs (which should be immutable; or also for immutable classes), you will need to fetch, re-create, and overwrite:
long theId = ...
var oldItem = yourDictionary[theId]; // fetch
var newItem = new SomeType(oldItem.Id, oldItem.Name, "England"); // re-create
yourDictionary[theId] = newItem; // overwrite
(obviously the re-create line needs tweaking to your particular objects)
In the evil evil world of mutable structs (see comments), you can mutate once it is in a variable:
long theId = ...
var item = yourDictionary[theId]; // fetch
item.Country = "England"; // mutate
yourDictionary[theId] = item; // overwrite
dictionary[memberID].Location = "Italy";
Well, I can't outcode Marc or Jon but here's my entry: (I used City instead of Country but the concept is the same.)
using System;
using System.Collections.Generic;
public class MyClass
{
public static void Main()
{
var dict = new Dictionary<int, Member>();
dict.Add(123, new Member("Jonh"));
dict.Add(908, new Member("Andy"));
dict.Add(456, new Member("Sarah"));
dict[456].City = "London";
Console.WriteLine(dict[456].MemberName + " " + dict[456].City);
Console.ReadKey();
}
}
public class Member
{
public Member(string name) {MemberName = name; City="Austin";}
public string MemberName { get; set; }
public string City { get; set; }
// etc...
}

Select new object as a parameter while preserving it's run-time-generated type

Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.

Categories

Resources