|
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)
|
|
|
|
|
|
If I had seen this:
private List<DateTime> _someList = null;
public List<DateTime> SomeList
{
get
{
_someList = _someList ?? new List<DateTime>();
return _someList;
}
} My brain might not have missed your train.
«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)
|
|
|
|
|
There are good days and there are bad days.
And there are days every day!
|
|
|
|
|
Fine, which one of those 3 methods do you prefer? Or do you have another suggestion?
public object GetExpensiveAndNonNullObject()
{
if (expensiveObjectBasedOnPrivateState == null)
expensiveObjectBasedOnPrivateState = new object();
return expensiveObjectBasedOnPrivateState;
}
object expensiveObjectBasedOnPrivateState;
Or
public object GetExpensiveAndNonNullObject()
{
expensiveObjectBasedOnPrivateState = expensiveObjectBasedOnPrivateState ?? new object();
return expensiveObjectBasedOnPrivateState;
}
object expensiveObjectBasedOnPrivateState;
Or
public object GetExpensiveAndNonNullObject()
{
return expensiveObjectBasedOnPrivateState ?? (expensiveObjectBasedOnPrivateState = new object());
}
object expensiveObjectBasedOnPrivateState;
Or.. something else?
modified 21-Oct-17 3:22am.
|
|
|
|
|
Super Lloyd wrote: return field ?? (field = Create());
While this code line is so trivial that there's no risk of misunderstanding it, I try to avoid making use of the expression-character of an assignment.
Method 2 I dislike for the same reason as you. So it's Method 1 for me.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Sascha Lefèvre wrote: I try to avoid making use of the expression-character of an assignment. There is something about that usage that gives me a sense of "spooky action at a distance"
«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)
|
|
|
|
|
I am using a DataRow to get and set values (based on the manually typed data table from How to Manually Create a Typed DataTable[^]. The fields in the datarow can have different attributes that I would need to track, the main one being a maximum number of characters for some strings, but different versions have different lengths. I was trying to use Attributes to help with this, but I am stuck with how to be able to set/determine the 'version' when going through the attributes. I'd like to be able to set the version on the dataset as a whole and have the fields of the data row be able to use that to determine how to get values.
Here's how I setup my attribute class:
[AttributeUsage(AttributeTargets.Property, AllowMultiple =true)]
public class MaxLengthAttribute : Attribute
{
public string Version;
public int MaxLength;
public MaxLengthAttribute(string version, int max)
{
Version= version;
MaxLength = max;
}
} Here's what the data row looks like and how I am envisioning this would work.
public CustomerRow : DataRow
{
[MaxLength("1.0", 40)]
[MaxLength("2.0", 45)]
public string Name
{
get
{
if(base["Name"] == DBNull.Value) throw new NullException("Name is null.");
return ((TruncatedString)base["Name"]).Value;
}
}
} I created a class called TruncatedString that has a string and bool, which i am using as the data type for the field in the data row. In the TruncatedString I am trying to get the version of the data set and the max length attribute to find out how long my field can be:
public class TruncatedString
{
public bool AutoTruncate;
private string _Value;
public string Value
{
get
{
if(AutoTruncate)
{
int maxLength = ???
if(maxLength >= 0 && Value.Length > maxLength) return _Value.Substring(0, maxLength);
}
return _Value;
}
set { _Value = value; }
}
}
I am not sure if Attributes are the best way to go with this setup, but I do need to be able to store the untruncated string as well as a way to get the truncated but be able to have different lengths for different versions. I also want to be able to do other attributes, for example a field that was added in version 2.0 but not available in 1.0.
|
|
|
|
|