|
|
Thanks for bring this up; I wasn't aware they existed.
That's interesting: an immutable class with a value comparison.
That's basically a value type that can be inherited from, which is a big limitation - there are times when it would have been nice to inherit from int to add specific functionality without having to encapsulate it and manually build the comparison operators (since classes always default to a reference equality) - a record does a property-by-property value comparison for you, and is by default immutable. Nice.
Not sure what I'll use them for yet, but they are well worth remembering for future projects.
Good question!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
You're welcome!
Sing the praise when you do!
|
|
|
|
|
I might be missing the benefit because records, I suspect, target things like data manipulation / EntityFramework, which is as far as possible from what I am doing now, both at work and home....
|
|
|
|
|
I guess they are quite handy to quickly create non trivial dictionary key...
for example:
public static WeakDictionary<TKey, TValue> Get<TKey, TValue>()
where TValue : class
where TKey : notnull
{
var k = new Key(typeof(TKey), typeof(TValue));
if (!caches.TryGetValue(k, out var result))
{
lock (caches)
{
if (!caches.TryGetValue(k, out result))
{
result = new WeakDictionary<TKey, TValue>();
caches[k] = result;
}
}
}
return (WeakDictionary<TKey, TValue>)result;
}
record class Key(Type key, Type value);
readonly static ConcurrentDictionary<Key, IWeakCache> caches = new();
|
|
|
|
|
trying to imagine what goes on in this code: smoke started coming out of my ears
«The mind is not a vessel to be filled but a fire to be kindled» Plutarch
|
|
|
|
|
Super Lloyd wrote: Though I wonder why it's available only to records.
Not the case since C# 10:
Beginning with C# 10, a left-hand operand of a with expression can also be of a structure type or an anonymous type.
Still not available for regular classes though.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
|
You might enjoy this discussion on Reddit: What is the point of records [^].
imho, Records major potential benefit is: immutability.
«The mind is not a vessel to be filled but a fire to be kindled» Plutarch
|
|
|
|
|
Good find Bill, thanks!
|
|
|
|
|
That's the point of many functional building blocks; they're simple by design; expressly so.
In contrast to reference types; a value type, like a record, avoids the guess work that's inherent with state management. On the surface that may appear pointless; why would we want something less powerful and similarly not mutable?
Simply said it's difficult to impossible to mathematically reason about a type whose state is in flux. The functional programming approach is always to build parts that are as simple as they can be; because the more simplistic something is, the greater its potential for composition, and the easier for us to mathematically reason about the outcome of that composition.
Functionally constructed applications are a composition of many simplistic parts:
- their simplicity is what simplifies reasoning
- whereas their ease of composition, is what enables the power.
As example:
The lack of flux in functional programming is what typically makes functional code far easier to multithread than OOP code; because.. for example: all the contention that's typical to referencing is avoided as a default -- by explicit design there is no likelihood for data race conditions between functionally code threads.
As to why "with" is only available to records?
It's because records, unlike reference types, are by design immutable; so without something like "with"; changing values would be far more difficult for record types, and there typically would be no standard approach that everyone could rely upon. With reference types; the standard is; getters and setters. "with" is essentially the setter standard for record types.
Side note:
"With" design is not an entirely new concept; it for the most part is a very simplistic introduction of Functional Optics (e.g. Lenses) to C#. Many of these more complex functional optics can be constructed in C#; although a lot of boiler plate is still required to take full advantage of it. Hopefully this is a gap that could be covered in a future C# release i.e. automatic code generation of functional optics for record types.
|
|
|
|
|
cool.
But you got one thing wrong though. Records are NOT immutable. They can be immutable, as much as any other class can be. 'with ' syntax make it easier to use the immutable versions one could say though.
|
|
|
|
|
Not quite...
In its simplest syntactic form i.e. positional syntax; records are immutable by default, for example:
record User(int Id, string LastName);
However where you are correct, is that records defined with nominal creation syntax are mutable as a default, for example:
public record User {
public int Id { get; set; }
public string LastName { get; set; }
}
To stay competitive with other languages e.g. Swift, Microsoft have included additional syntactical flexibility to, e.g. control immutability at a field level, for example:
public record User {
public int Id { get; init; }
public string LastName { get; set; }
}
However, the reason for records according to Microsoft is as follows;:
While records can be mutable, they're primarily intended for supporting immutable data models.
C# 10; again brings more syntactic flexibility around records, e.g. the addition of:
record struct ...
readonly record struct ...
Functional programmers will as a default lean on positional syntax; because its both shorter and guarantees immutability, and is quite similar to other functional languages. Whereas the OOP programmer would be more familiar with the nominal syntax and its behavior.
As for with ; its purpose is to provide a non destructive way to do mutation; or stated differently; provide a simplified syntactic solution to create a new copy of a data structure; where some of the field values are changed in process; without of course harming the integrity of the source data structure.
modified 7-Jan-22 16:53pm.
|
|
|
|
|
Quick preamble, I last coded in C# probably 10 years ago. A lot has changed.
I am using SyncFusions drop down box to return an index value from a list.
The variables ddlIndex, and feederDropValue both return the correct values.
<SfDropDownList DataSource = "@lines" TValue="string" TItem="FeederTableQuery" Placeholder="Select a feeder" @bind-Value = "@FeederDropValue" @bind-Index="@ddlIndex">
<DropDownListFieldSettings Text="FeederId" Value="FeederId"></DropDownListFieldSettings>
</SfDropDownList>
<DashboardLayoutPanel Column="2" Row="1" SizeX="2" SizeY="2">
<HeaderTemplate>
<div class="e-header-text">Feeder Details</div>
<div class="header-border"></div>
</HeaderTemplate>
<ContentTemplate>
<div class="panel-content">
<p>Selected value was @FeederDropValue</p>
<p>Selected index was @ddlIndex</p>
@lines.ElementAt(ddlIndex).ToString();
</div>
</ContentTemplate>
</DashboardLayoutPanel>
@code
{
private int? ddlIndex { get; set; } = 0;
public string FeederDropValue;
private IEnumerable <FeederTableQuery> lines { get; set; }
}
I want to the print out what is at that index value, however I get an error when trying select elementAt:
cannot convert from 'int?' to 'System.Index
I admit I not profficent with IEnumerables (probably where I am going wrong), but any help is much appreciated!
|
|
|
|
|
try using lines[(int)ddlIndex]
You can't use nullable vars without casting them.
I probably wouldn't make the ddlIndex nullable, because that means if it happens to be null, it will throw an InvalidOperationException when you try to index a collection.
".45 ACP - because shooting twice is just silly" - JSOP, 2010 ----- You can never have too much ammo - unless you're swimming, or on fire. - JSOP, 2010 ----- When you pry the gun from my cold dead hands, be careful - the barrel will be very hot. - JSOP, 2013
modified 3-Jan-22 8:24am.
|
|
|
|
|
This worked - thanks.
I admit I understand why a nullable int caused potential conflict and hence the error, I still am not grasping the navigation of the
IEnumerable object.
I have created the method:
public string getDetails(int ddIndex)
{
FeederTableQuery q = lines.[ddIndex];
return q.ConductorName.ToString();
}
Where I want to pass the previously mentioned (and cast) ddlIndex variable.
However, the above method causes the error:
Error (active) CS1001 Identifier expected
Or, if you remove the "." e.g:
lines[ddIndex]
(how I traditionally would have thought of indexing something) it complains:
Error (active) CS0021 Cannot apply indexing with [] to an expression of type 'IEnumerable<FeederTableQuery>
Feeling dumb, guidance appreciated.
|
|
|
|
|
Sorry, forgot you were using IEnumerable . Try this:
FeederTableQuery q = lines.ElementAt((int)ddlIndex);
".45 ACP - because shooting twice is just silly" - JSOP, 2010 ----- You can never have too much ammo - unless you're swimming, or on fire. - JSOP, 2010 ----- When you pry the gun from my cold dead hands, be careful - the barrel will be very hot. - JSOP, 2013
|
|
|
|
|
int? is a nullable int and there is no implicit conversion from that to an index value.
Instead, you need to check that it isn't null, and use the Value property to get the "base integer" you need to index the collection.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
-1 is often used to indicate an "invalid" int (by convention) instead of burdening oneself with nulls.
For the trolls, this assumes one doesn't need negatives. Think of .SelectedIndex if that helps your comprehension.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
Yep. Or string.IndexOf as well.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
While I`m running/debugging a c# app I`m developing in VS that has a while loop with no exit, a program freeze takes place when the while statement is reached, at the same time the OS continues to be responsive, is that because the computer is running an 64 bit OS (with threads on multicore). Would a while statement like that cause a 32 bit OS to freeze?
Also if I`m running a recursive function with no exit from recursion the program will break with an `stack overflow` error. Why is the debugger interrupting the program in the case of the recursive function and doesn`t in the case of a while loop?
|
|
|
|
|
With a "stack overflow", even the debugger can't continue.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
with the right pair of boots and equipment for hostile environment you never know
|
|
|
|
|
A while loop isn't an error as far as the system is concerned - it just means that your display can't be updated and user input is ignored until the loop exist and the event handler is finished. The system doesn't "know" how long you will be looping for and has to assume that you intended that to happen!
Unbounded recursion is different: each recursive call uses up an amount of some special memory called the stack - and when that runs out (and it does, pretty quickly) you app has to stop running because it can't execute any more code; it needs the stack to do anything. So it throws an exception - Stack overflow - which the debugger picks up for you and stops you app so you can see what is happening.
You shouldn't use big loops in event handlers - they should be moved to a second thread so that the UI thread can continue to update the display and deal with user input. Have a look here: BackgroundWorker Class (System.ComponentModel) | Microsoft Docs[^]
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Thanks Griff that`s a good theoretical explanation
|
|
|
|