|
I wondered for at least 2 minutes whether I should bother reply to that stupid comment.
I decided to.
Yes, thread safety in that minimalist sample would distract the viewer from my problem. So the lack of it is a feature, not a bug.
modified 20-Oct-17 3:59am.
|
|
|
|
|
Super Lloyd wrote: it is a feature, not a bug.
Ah! It's in the manual!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Super Lloyd wrote: that stupid comment. Bernard has never made a stupid comment since he was ten years old; we're fortunate he takes the time to share his deep technical knowledge !
«While I complain of being able to see only a shadow of the past, I may be insensitive to reality as it is now, since I'm not at a stage of development where I'm capable of seeing it. A few hundred years later another traveler despairing as myself, may mourn the disappearance of what I may have seen, but failed to see.» Claude Levi-Strauss (Tristes Tropiques, 1955)
|
|
|
|
|
Thanks.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|
Which is not always a requirement; you don't want to introduce more complexity if this code is only ever called by the UI-thread. And no, you don't add it because of "possible future use" - that's the most lame excuse ever to bloat the code.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
#3 is what ReSharper suggests if you type #1.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Does resharper also explain how the first is C# 2.0 compliant, and that option 3 would require at least .NET 3.0? The first example will run on a Windows 2000 machine (if all updates are installed). The second third* won't, ever.
*) third; having trouble with counting and very large numbers
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Eddy Vluggen wrote: The second third* won't, ever.
Yes it will. It's syntactical sugar; so long as the compiler supports it, the compiled code would run on .NET 1.0 if you wanted to.
IL for #1:
IL_0000: ldarg.0
IL_0001: ldfld Foo.field
IL_0006: brtrue.s IL_0014
IL_0008: ldarg.0
IL_0009: ldarg.0
IL_000A: call Foo.Create
IL_000F: stfld Foo.field
IL_0014: ldarg.0
IL_0015: ldfld Foo.field
IL_001A: ret IL for #3:
IL_0000: ldarg.0
IL_0001: ldfld Foo.field
IL_0006: dup
IL_0007: brtrue.s IL_0019
IL_0009: pop
IL_000A: ldarg.0
IL_000B: ldarg.0
IL_000C: call Foo.Create
IL_0011: dup
IL_0012: stloc.0
IL_0013: stfld Foo.field
IL_0018: ldloc.0
IL_0019: ret
The only difference between the two is that #3 uses dup to avoid calling ldfld twice. It's effectively equivalent to:
object temp = field;
if (temp == null)
{
temp = Create();
field = temp;
}
return temp;
(But yes, R# has an option to change the C# language level used by its suggestions, so you could prevent this suggestion if you really wanted to.)
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
modified 20-Oct-17 8:26am.
|
|
|
|
|
Richard Deeming wrote: Yes it will. It's syntactical sugar; so long as the compiler supports it, the compiled code would run on .NET 1.0 if you wanted to So you need a newer compiler, and then you can use the older runtime.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Yes. That's how a lot of the changes to C# are implemented - the compiler recognises the newer code, and compiles it into something that will run on an older version of the framework.
String interpolation; pattern matching; the enhanced "switch" statement; the null-conditional operator - they all compile to IL that will work on older framework versions.
Some of the changes require additional libraries to work - eg: ValueTuple on < 4.7 and async on 4.0 both need extra NuGet packages, and in some cases, changes to class names.
And some changes require additional changes to the CLR or the BCL. But those tend to be quite rare, because the language team are reluctant to make those kinds of changes.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Richard Deeming wrote: String interpolation; pattern matching; the enhanced "switch" statement; the null-conditional operator - they all compile to IL that will work on older framework versions. That's what "syntactical sugar" implies; still, without installing the newer compiler (which is usually done by updating the runtime) you're not going to compile that code.
Won't mean much to you; you simply compile and distribute your assembly, as traditional on Windows. Those machines will mostly be running v4 of the runtime or better. I'm currently on a Ubuntu-machine, distributing source
Richard Deeming wrote: And some changes require additional changes to the CLR or the BCL. But those tend to be quite rare, because the language team are reluctant to make those kinds of changes. Microsoft has always been a fan of backward compatibility. TheOldNewThing blog has a nice section on how much effort was put into keeping popular games applications working in Windows 95.
*) ..and I check the dictionary, which clearly states "English". Ah, the US-version..
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
The new compiler is on the developer machine
The older runtime is on the client computer.
The client does NOT need the new compiler.
Problem... solved? I believe....
modified 21-Oct-17 2:53am.
|
|
|
|
|
Haha!
Problem is I dislike Resharper, so I would have never know!
|
|
|
|
|
All three methods require a comparison for every access of the property.
Since it should always be defined then the set semantics and ctors should define that rather than the get.
|
|
|
|
|
jschell wrote: All three methods require a comparison for every access of the property. The basic idea of a singleton; do we have an object? If not, make one. Return it.
The OP doesn't specifically request a singleton, otherwise I could point to the "static" modifier, but still for practical purposes seems to be used in that way.
jschell wrote: Since it should always be defined then the set semantics and ctors should define that rather than the get. Nah, a property that defines a getter is going to return a value, and in the exceptional case of an error it will raise an exception. Which parts of those semantics are unclear?
A get (property) can be part of an Interface. It demands you keep your promises
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Eddy Vluggen wrote: Nah, a property that defines a getter is going to return a value, and in the exceptional case of an error it will raise an exception. Which parts of those semantics are unclear?
Perhaps my response was unclear.
I wasn't suggesting that the getter should not exist.
I was suggesting that the ctor or other usages in the class should insure that the attribute always has a value.
If a setter was added then that too should insure that a value always exists.
Then in terms of the original question, since the class insures that a value always exists, the getter should not check for that. And that would be more efficient.
|
|
|
|
|
jschell wrote:
I was suggesting that the ctor or other usages in the class should insure that the attribute always has a value.
If a setter was added then that too should insure that a value always exists. So, instead of testing in the getter, you apply this in ctor, other useages, and the setter? Why, is it more DRY than putting that test in the getter?
jschell wrote: And that would be more efficient. If you had said that the ctor is used to ensure the value is there, you'd be more efficient, since the test no longer exists and you always have a value. That wouldn't be very lazy though, defeating the original purpose.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Eddy Vluggen wrote: So, instead of testing in the getter, you apply this in ctor, other useages, and the setter? Why, is it more DRY than putting that test in the getter?
Don't believe that was what was originally asked. "Satisfying" was the word used.
If the only criteria is DRY then the getter might be ok. However one might question what happens if the class itself wants to use the attribute. Must it always use the getter only? Or does it check the attribute.
When efficiency is also a concern, then in general, my assumption is that getters are used more than setters. Consequently reducing code in setters makes it more efficient.
Eddy Vluggen wrote: That wouldn't be very lazy though, defeating the original purpose.
That is a very valid point.
|
|
|
|
|
As I specify in my question I dislike that, and it was not the question. I think it's a matter of preference which is so for 2 reasons.
Just for your sake I will elaborate on my reasons.
Reason #1: Maybe creating the object is expensive and I want to delay its creation as much as possible? To improve startup time, for example, as it is usually the main strength of lazy initialisation.
Reason #2 I found the code below distasteful (a subjective matter, to be sure, but I think you will understand on seeing it), prone to refactor error, possibly needlessly expensive.
public class MyClass
{
public MyClass()
{
E = new object();
}
public object A { get; set; }
public object B { get; set; }
public object C { get; set; }
public object D { get; set; }
public object E { get; private set; }
public object F { get; set; }
public object G { get; set; }
public object H { get; set; }
}
Remark if you have trouble seeing the point of my example above, silently refactor in your head E to NonNullPropertyThatIsExpensiveToInitialiseAndBasedOnPrivateState
modified 21-Oct-17 3:29am.
|
|
|
|
|
In case you want to instantiate E only once and only during instantiation, features of newer C# versions can mitigate the issues.
You can remove the private setter:
public object E { get; } You could use a backing field and mark that read-only (that's not possible with properties, even when they have no explicit setter), and use an expression body for the property:
private readonly object _E = new object();
public object E => _E; If someone removes the assignment of the backing field and compiles that, he'll receive a compiler warning:Quote: warning CS0649: Field 'MyClass._E' is never assigned to, and will always have its default value null When you use the project option Treat warnings as errors = All , it won't compile at all, thus the instantiation cannot be forgotten.
Well, the initialization of E stays expensive anyway.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|
personally I like that better
public object A { get; } = new object();
But we are missing the point here, I was wonder about lazy initialisation, i.e. fields which are initialised on demand, as opposed to when the object is created!
|
|
|
|
|
Bernhard Hiller wrote: that's not possible with properties, even when they have no explicit setter
That's not necessary when the property has no explicit setter. The backing field can't be accessed (except via reflection), and it's marked as readonly automatically.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I can't help thinking that these examples are better expressed with a public method, and private field:
internal object _field = null;
public object Field
{
get
{
return _field;
}
}
public void SetField(object ofield)
{
_field = ofield;
} imho, clearer intent, and greater 'separation of concerns,' is expressed this way.
But, also: why even use a Property unless there is a compelling reason to so; a reason like needing to throw an error when the value is null; a reason like needing PropertyChange notification.
«While I complain of being able to see only a shadow of the past, I may be insensitive to reality as it is now, since I'm not at a stage of development where I'm capable of seeing it. A few hundred years later another traveler despairing as myself, may mourn the disappearance of what I may have seen, but failed to see.» Claude Levi-Strauss (Tristes Tropiques, 1955)
|
|
|
|
|
Did you miss something Bill? Nowhere in your code SetField() is called!
your code is exactly the same as mine minus the crucial part, the one I wonder about, when/where/how SetField() is called!
My question was about (elegant) lazy initialisation after all!
modified 21-Oct-17 3:24am.
|
|
|
|
|
Super Lloyd wrote: Did you miss something Bill? Yeah Sorry about that ! cheers, mate ...
«While I complain of being able to see only a shadow of the past, I may be insensitive to reality as it is now, since I'm not at a stage of development where I'm capable of seeing it. A few hundred years later another traveler despairing as myself, may mourn the disappearance of what I may have seen, but failed to see.» Claude Levi-Strauss (Tristes Tropiques, 1955)
|
|
|
|