Open AlgorithmsAreCool opened 6 years ago
The C# compiler can do this without any ceremony, just like the VB.NET compiler does:
Module XmlHelpers
Public Sub NormalizeDocument(ByVal doc As XDocument)
For Each element In doc.Descendants()
Normalize(element.Name)
Next element
End Sub
Public Sub Normalize(ByRef name As XName)
name = XName.Get(name.LocalName.ToLower(), name.NamespaceName.ToLower())
End Sub
End Module
What VB.NET does is capture the value of the property into a local, pass that by ref
and then assign that local back to the property. It's clean and works pretty well, but it doesn't reflect the changes made immediately by the callee until after that method returns successfully.
There's no reason that this should require new keywords. The existing ref and out keywords would work just fine (they specify intent, not implementation).
As @HaloFour pointed out VB.Net already allows this, and the same approach (of creating a local variable) could be used - if this was something that C# wanted to do.
The code would look like this:
public static void NormalizeDocument(XDocument xdoc)
{
// normalize names to lowercase
foreach (var element in xdoc.Descendants())
{
Normalize(ref element.Name);
}
}
private static void Normalize(ref XName name)
=> name = XName.Get(name.LocalName.ToLower(), name.Namespace.NamespaceName);
and the compiler would convert the syntactic sugar into this:
public static void NormalizeDocument(XDocument xdoc)
{
// normalize names to lowercase
foreach (var element in xdoc.Descendants())
{
var name = element.Name;
Normalize(ref name);
element.Name = name;
}
}
But ... I remember this being intensely discussed back when C# was a brand new language and I believe there were some pretty compelling reasons why the C# designers decided not to support this. Unfortunately, I can't remember the details and I haven't been able to find any references with a quick search.
I guess it's possible that opinions will have changed in the intervening time.
There's no reason that this should require new keywords. The existing ref and out keywords would work just fine (they specify intent, not implementation).
Last time this was asked for, I pointed out that allowing ref
and out
on properties would majorly break thread-safe code. I think it could be useful but a new keyword should be required to prevent such mistakes.
@scalablecory
properties would majorly break thread-safe code.
Only if the expectation is that the property would be written to immediately. I can't imagine that there's much code that has that expectation and I would question any label of "thread-safe" applied to that code. I doubt most people realize that ref
parameters are written immediately, let alone rely on it.
I had a similar idea (#3084), but a bit expanded to allow read only and write only properties. Copied from there and renamed with the scheme used here:
interface IReadPropertyReference<T>
{
T Get();
T Value { get; } // Alternative
}
interface IWritePropertyReference<T>
{
void Set(T value);
T Value { set; } // Alternative
}
public sealed class PropertyReference<T> : IReadPropertyReference<T>, IWritePropertyReference<T>
{
}
Related idea - it would be nice to create delegates which bind directly to the property getter without using reflection:
class Class
{
int Prop { get; set; }
}
void Foo(Func<int> get, Action<int> set) { }
var c = new Class();
Foo(c.Prop, c.Prop); // something like that - I'm sure a better (more explicit) syntax can be devised
@TahirAhmadov That's covered by https://github.com/dotnet/csharplang/discussions/84.
I would love this feature to be used in local variables, because writing same class and name is tedious.
public class GameDetailData
{
public int Year { get; set; }
}
// Currently, when you need to update the value for GameDetailData.Year, you need to write this.
// I know this sample code is bad, but hey.
string nintendo = GetGameName(args);
switch (nintendo)
{
case "Super Mario Odyssey":
GameDetailData.Year = 2017;
break;
case "Monster Hunter Rise":
GameDetailData.Year = 2021;
break;
// And so on...
}
// If property can be referrable, you can write this method like this:
ref var year = ref GameDetailData.Year;
string nintendo = GetGameName(args);
switch (nintendo)
{
case "Super Mario Odyssey":
year = 2017;
break;
case "Monster Hunter Rise":
year = 2021;
break;
// And so on...
}
You can already do
string nintendo = GetGameName(args);
GameDetailData.Year = nintendo switch
{
"Super Mario Odyssey" => 2017,
"Monster Hunter Rise" => 2021,
// And so on...
}
A while back I had a bad idea. But I think there was something useful in the concept.
Today I had some code that looked like this:
Existing code
It occurred to me that if
Name
was a field I could just write a ref version like this.So why not allow this convenience?
Proposed code
The idea being that the compiler gives us a struct similar to the following, but the expression is an lValue just like a ref field.
A few thoughts
refprop
is not the best name, I'm open to suggestions, including justref
.propref
expression should translate to subsequent calls to the underlying getter.in
.propin
would only work if there was a public setter.propref
is only allowed if there is both a getter and a setter.propout
is useful by itself.properf
. But that might be too much.An open question is should there be a way to access the underlying property name?
Good Idea/Bad Idea?