Turn a variable into constant - c#

Does there exist any c# type/structure that enables a variable turning into a constant?
Maybe by setting a flag which fixes the value.
Example code:
foreach (Person p in group)
{
if (p.Mood.Equals("bad"))
{
group.Mood = "bad";
group.Mood.Fix(); // This should disable any further changing on group.Mood
}
else
{
group.Mood = "good";
}
}
The mood of the whole group should be "bad" if one persons mood is bad.
I know that I can implement something like this by myself but I use it that often that I can imaging that such a structure already exists.
I hope you're all in a good mood! ;)

No, the variable is made constant upon declaration. However, you could get something close with a property setter:
public string Mood
{
set
{
if(mood != "Bad")
mood = value;
}
}

I would advice against doing this for the following reasons:
A property with a setter should be allowed to be set
A property with a setter should not simply ignore the value given to it
Secondly, why do you even look at more people in your collection if you've already established the final value?
I would instead change the code into one of the three following variants:
group.Mood = "good";
foreach (Person p in group)
{
if (p.Mood.Equals("bad"))
{
group.Mood = "bad";
break;
}
}
or this:
string groupMood = "good";
foreach (Person p in group)
{
if (p.Mood.Equals("bad"))
{
groupMood = "bad";
break;
}
}
group.Mood = groupMood;
or this:
if (group.Any(p => p.Mood == "bad"))
group.Mood = "bad";
else
group.Mood = "good";
If instead you want such a "Popsicle" type, here's one which you can try with LINQPad:
void Main()
{
var i = new Popsicle<int>(10);
i.Value = 25;
i.Value = 33;
i.Dump();
i.Freeze(FrozenBehavior.IgnoreNewValues);
i.Value = 17;
i.Dump();
i = new Popsicle<int>(10);
i.Freeze(FrozenBehavior.ThrowException);
i.Value = 17;
i.Dump();
}
public class Popsicle<T>
{
private T _Value;
private FrozenBehavior? _FrozenBehavior;
public Popsicle(T value = default(T))
{
_Value = value;
}
public T Value
{
get
{
return _Value;
}
set
{
if (_FrozenBehavior.HasValue)
{
switch (_FrozenBehavior.GetValueOrDefault())
{
case FrozenBehavior.IgnoreNewValues:
break;
case FrozenBehavior.ThrowException:
throw new InvalidOperationException("This Popsicle<T> has been frozen and configured to throw exceptions if an attempt is made to change its value");
default:
throw new InvalidOperationException("This Popsicle<T> is in an invalid frozen state");
}
}
else
_Value = value;
}
}
public void Freeze(FrozenBehavior behavior = FrozenBehavior.ThrowException)
{
if (!_FrozenBehavior.HasValue)
_FrozenBehavior = behavior;
else
throw new InvalidOperationException("This Popsicle<T> has already been frozen, cannot re-freeze");
}
public override string ToString()
{
if (!_FrozenBehavior.HasValue)
return _Value + " (unfrozen)";
else switch (_FrozenBehavior)
{
case FrozenBehavior.IgnoreNewValues:
return _Value + " (frozen: ignore)";
case FrozenBehavior.ThrowException:
return _Value + " (frozen: throw)";
default:
throw new InvalidOperationException("This Popsicle<T> is in an invalid frozen state");
}
}
}
public enum FrozenBehavior
{
IgnoreNewValues,
ThrowException
}

With LinQ this can be done in a short way. Check if there is any person that has a bad mood, if so set the group-mood to bad, otherwise good.
group.Mood = group.Any(p => p.Mood == "bad") ? "bad" : "good";

Related

error CS0115: 'PopupField<T>.ExecuteDefaultAction(EventBase)': no suitable method found to override

I have an error with my script, Unity tells me this:
Library\PackageCache\com.unity.package-manager-ui#1.9.11\Editor\Sources\UI\Common\PopupField.cs(118,33): error CS0115: 'PopupField.ExecuteDefaultAction(EventBase)': no suitable method found to override
and this is the script:(I'm a noob with scripts)
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
namespace UnityEditor.PackageManager.UI
{
internal class PopupField<T> : BaseTextElement, INotifyValueChanged<T>
{
private readonly List<T> m_PossibleValues;
private Func<T, string> m_Callback;
private EventCallback<ChangeEvent<T>> m_valueCallback;
private T m_Value;
public T value
{
get { return m_Value; }
set
{
if (EqualityComparer<T>.Default.Equals(m_Value, value))
return;
if (!m_PossibleValues.Contains(value))
throw new ArgumentException(string.Format("Value {0} is not present in the list of possible values", value));
m_Value = value;
m_Index = m_PossibleValues.IndexOf(m_Value);
if (m_Callback != null)
text = m_Callback(m_Value);
else
text = m_Value.ToString();
#if UNITY_2018_3_OR_NEWER
MarkDirtyRepaint();
#else
Dirty(ChangeType.Repaint);
#endif
}
}
private int m_Index = -1;
public int index
{
get { return m_Index; }
set
{
if (value != m_Index)
{
if (value >= m_PossibleValues.Count || value < 0)
throw new ArgumentException(string.Format("Index {0} is beyond the scope of possible value", value));
m_Index = value;
this.value = m_PossibleValues[m_Index];
}
}
}
/// <summary>
/// Callback that will return the string to be set in the field's label.
/// </summary>
/// <param name="callback"></param>
public void SetLabelCallback(Func<T, string> callback)
{
m_Callback = callback;
}
private PopupField(List<T> possibleValues)
{
if (possibleValues == null)
throw new ArgumentNullException("possibleValues can't be null");
m_PossibleValues = possibleValues;
AddToClassList("popupField");
}
public PopupField(List<T> possibleValues, T defaultValue) :
this(possibleValues)
{
if (defaultValue == null)
throw new ArgumentNullException("defaultValue can't be null");
if (!m_PossibleValues.Contains(defaultValue))
throw new ArgumentException(string.Format("Default value {0} is not present in the list of possible values", defaultValue));
// note: idx will be set when setting value
value = defaultValue;
}
public PopupField(List<T> possibleValues, int defaultIndex) :
this(possibleValues)
{
if (defaultIndex >= m_PossibleValues.Count || defaultIndex < 0)
throw new ArgumentException(string.Format("Default Index {0} is beyond the scope of possible value", value));
// note: value will be set when setting idx
index = defaultIndex;
}
public void SetValueAndNotify(T newValue)
{
if (!EqualityComparer<T>.Default.Equals(newValue, value))
{
using (ChangeEvent<T> evt = ChangeEvent<T>.GetPooled(value, newValue))
{
value = newValue;
if (m_valueCallback != null)
m_valueCallback(evt);
}
}
}
public void OnValueChanged(EventCallback<ChangeEvent<T>> callback)
{
m_valueCallback = callback;
RegisterCallback(callback);
}
protected override void ExecuteDefaultAction(EventBase evt)
{
base.ExecuteDefaultAction(evt);
if (evt.GetEventTypeId() == MouseDownEvent.TypeId())
OnMouseDown();
}
private void OnMouseDown()
{
var menu = new GenericMenu();
foreach (T item in m_PossibleValues)
{
bool isSelected = EqualityComparer<T>.Default.Equals(item, value);
menu.AddItem(new GUIContent(item.ToString()), isSelected,
() => ChangeValueFromMenu(item));
}
var menuPosition = new Vector2(0.0f, layout.height);
menuPosition = this.LocalToWorld(menuPosition);
var menuRect = new Rect(menuPosition, Vector2.zero);
menu.DropDown(menuRect);
}
private void ChangeValueFromMenu(T menuItem)
{
SetValueAndNotify(menuItem);
}
}
}
See BaseTextElement
Experimental: this API is experimental and might be changed or removed in the future.
apparently it was ... That's why it is called Experimental
There currently is a PopupFied<T> already but again
Experimental: this API is experimental and might be changed or removed in the future.
Looks like a version issue. Check out the following link:
https://forum.unity.com/threads/unity-ui-elements-could-not-be-found-in-2018-3-0a6-beta-personal.550672/

NetSuite Custom Record Search Convert Results into List of .NET Objects

The only way I've been able to do it is through the reflection-based code below. I can't believe there's not an "easier NetSuite way" to do this though? Am I missing something basic?
After I perform a search on custom objects I get back an array of Record[], this can then be looped through and each item casted to a CustomObject.
The properties of the custom object are stored in the CustomRecord's customFieldList but the values are not immediately accessible you have to cast those the their real NetSuite type (like LongCustomFieldRef, DoubleCustomFieldRef, BooleanCustomFieldRef, StringCustomFieldRef, etc).
In order to not have to bother with this mess to get nice clean objects on my side I decided on the approach below:
Create classes with property names that match (including case) the NetSuite names and inherits from NetSuiteBase (defined below)
public class MyNetSuiteObject : NetSuiteBase //<-- Note base class
{
public string myProperty1 { get; set; }
public bool myProperty2 { get; set; }
public int myProperty3 { get; set; }
public static MyNetSuiteObject FromCustomSearchRecord(CustomRecord customRecord)
{
var ret = new MyNetSuiteObject();
ret.AssignProperties(customRecord);
return ret;
}
}
Create a base class which will inspect CustomRecords and apply property values to the .NET classes
public class NetSuiteBase
{
public void AssignProperties(CustomRecord customRecord)
{
var classProps = this.GetType().GetProperties();
foreach (var prop in classProps)
{
var propName = prop.Name;
var propValue = prop.GetValue(this, null);
//get the matching CustomFieldRef out of the customFieldList for the CustomRecord which matches our current property name
var myCustomFieldRef = customRecord.customFieldList.Where(c => c.scriptId == propName).FirstOrDefault();
if (myCustomFieldRef == null) continue;
//we can't get the value out until we cast the CustomFieldRef to its "actual" type.
var custType = myCustomFieldRef.GetType().Name;
switch (custType)
{
case "LongCustomFieldRef":
TrySetProperty(prop, ((LongCustomFieldRef)myCustomFieldRef).value.ToString());
break;
case "DoubleCustomFieldRef":
TrySetProperty(prop, ((DoubleCustomFieldRef)myCustomFieldRef).value.ToString());
break;
case "BooleanCustomFieldRef":
TrySetProperty(prop, ((BooleanCustomFieldRef)myCustomFieldRef).value.ToString());
break;
case "StringCustomFieldRef":
TrySetProperty(prop, ((StringCustomFieldRef)myCustomFieldRef).value.ToString());
break;
case "DateCustomFieldRef":
TrySetProperty(prop, ((DateCustomFieldRef)myCustomFieldRef).value.ToString());
break;
case "SelectCustomFieldRef":
TrySetProperty(prop, ((SelectCustomFieldRef)myCustomFieldRef).value.name.ToString());
break;
case "MultiSelectCustomFieldRef":
TrySetProperty(prop, ((MultiSelectCustomFieldRef)myCustomFieldRef).value.ToString());
break;
default:
Console.WriteLine("Unknown type: " + myCustomFieldRef.internalId);
break;
}
}
}
//Some of the NetSuite properties are represented as strings (I'm looking at you BOOLs), so we pass all the values from above
//as strings and them process/attempt casts
private void TrySetProperty(PropertyInfo prop, string value)
{
value = value.ToLower().Trim();
if (prop.PropertyType == typeof(string))
{
prop.SetValue(this, value);
return;
}
if (prop.PropertyType == typeof(bool))
{
if (value == "yes") value = "true";
if (value == "no") value = "false";
if (value == "1") value = "true";
if (value == "0") value = "false";
bool test;
if (bool.TryParse(value, out test))
{
prop.SetValue(this, test);
return;
}
}
if (prop.PropertyType == typeof(int))
{
int test;
if (int.TryParse(value, out test))
{
prop.SetValue(this, test);
return;
}
}
if (prop.PropertyType == typeof(double))
{
double test;
if (double.TryParse(value, out test))
{
prop.SetValue(this, test);
return;
}
}
if (prop.PropertyType == typeof(decimal))
{
decimal test;
if (decimal.TryParse(value, out test))
{
prop.SetValue(this, test);
return;
}
}
}
}
After performing a NetSuite search on custom objects, loop through the results and use the above classes to convert NetSuite result to a .NET class
for (int i = 0, j = 0; i < records.Length; i++, j++)
{
customRecord = (CustomRecord)records[i];
var myNetSuiteObject = MyNetSuiteObject.FromCustomSearchRecord(customRecord);
}
Is there some other "NetSuite way" to accomplish what I have above?
No, there is no "NetSuite" way. What you've done is far above and beyond the call of duty, if you ask me, and it's amazing. The NetSuite/SuiteTalk WSDL is just HORRENDOUS, and so many bad choices were made in the design process that I'm shocked that it was released as-is to developers without any questions raised. Below is one other example.
This is from the documentation for their SuiteTalk course, which reveals that when parsing the results of an Advanced search, they contain within them SearchRowBasic objects. Well within each SearchRowBasic object there are multiple fields. To access those fields' values, you have to take the first element of a SearchColumnField array. Why would you DO this? If there will only ever be ONE value (and they state in their documentation that yes indeed there will only ever be ONE value), WHY the heck would you make the return object an array instead of just passing back to the developer the value of the primitive type itself directly??? That's just plain bad API construction right there, forcing the developers to use SearchColumnField[0].searchValue every time rather than just SearchColumnField.searchValue!

Howto search through Properties of all kinds of types

I have a base class called Part and derived classes like Wire or Connector and many more that inherit from Part.
Now I want to implement a search function that searches all Properties of the derived classes for a string.
If necessary that string should be tried to be converted to the type of the Property. The Properties can also be Lists and should be searched on the first level.
class Part
{
public int Id { get; set; }
public string Name { get; set; }
}
class Wire : Part
{
public NumberWithUnit Diameter { get; set; }
public Weight Weight { get; set; }
}
class Connector : Part
{
public List<Part> ConnectedParts { get; set; }
}
I know how to generally search through the Properties of base types with Reflection like this
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is string)
{
if (string.Equals(value, searchString))
return true;
}
else if (value is int)
{
int v;
if (int.TryParse(searchString, out v))
{
if(v == (int) value)
return true;
}
}
}
return false;
}
But that would be a long list of types and I have Properties of Type Weight for instance and many more. Is there some kind of general way to search without casting all types?
Consider going the opposite direction with your conversion. Rather than converting your search string into each possible value, just convert the value into a string:
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is IEnumerable)
{
// special handling for collections
}
else if(value != null)
{
string valueString = value.ToString();
if (string.Equals(valueString, searchString))
return true;
}
}
return false;
}
Besides working pretty well for most built-in types, the only thing you have to do to get it to work for Weight, etc. is make sure they implement ToString().
Another solution would be to use TypeDescriptor:
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is IEnumerable)
{
// special handling for collections
}
else if(value != null)
{
object searchValue = null;
try
{
searchValue = TypeDescriptor.GetConverter(value).ConvertFromString(searchString);
} catch {}
if (searchValue != null && object.Equals(value, searchValue))
return true;
}
}
return false;
}
TypeDescriptor works well for most built-in types, but requires extra work if you're dealing with custom types.
I think the following should cover the most of the practical scenarios:
public static bool SearchProperties(object target, string searchString)
{
if (target == null) return false;
// Common types
var convertible = target as IConvertible;
if (convertible != null)
{
var typeCode = convertible.GetTypeCode();
if (typeCode == TypeCode.String) return target.ToString() == searchString;
if (typeCode == TypeCode.DBNull) return false;
if (typeCode != TypeCode.Object)
{
try
{
var value = Convert.ChangeType(searchString, typeCode);
return target.Equals(value);
}
catch { return false; }
}
}
if (target is DateTimeOffset)
{
DateTimeOffset value;
return DateTimeOffset.TryParse(searchString, out value) && value == (DateTimeOffset)target;
}
var enumerable = target as IEnumerable;
if (enumerable != null)
{
// Collection
foreach (var item in enumerable)
if (SearchProperties(item, searchString)) return true;
}
else
{
// Complex type
var properties = target.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetMethod == null || property.GetMethod.GetParameters().Length > 0) continue;
var value = property.GetValue(target);
if (SearchProperties(value, searchString)) return true;
}
}
return false;
}
I will give you one different idea to do it.
You could try something like that:
private bool SearchProperties<T, W>(T part, W searchValue) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
if (typeof(W) == prop.PropertyType)
{
var value = prop.GetValue(part, null);
if (searchValue.Equals(value))
return true;
}
}
return false;
}
You need to call the method like this:
private void button12_Click(object sender, EventArgs e)
{
Part p = new Part();
p.Id = 2;
p.Name = "test";
p.bla = new Bla();
SearchProperties<Part, int>(p, 2);
}
And if you need to compare the complex properties (Weight, ...) by a different way from GetHashCode you could override the method Equals or the == operator.
class Weight
{
public int Id { get; set; }
public override bool Equals(object obj)
{
return Id == ((Weight)obj).Id;
}
}

How to access a dynamic object's property dynamically (by variable)?

I have a class named Configurationwhich inherits from DynamicObject. It it a Dictionary<string, object>. In the constructor, it reads a text file and loads all the values from it splitted by =, so you can create dynamic configuration objects like this:
dynamic configuration = new Configuration("some_file.ini");
Here is my full class:
public sealed class Configuration : DynamicObject
{
private string Path { get; set; }
private Dictionary<string, object> Dictionary { get; set; }
public Configuration(string fileName)
{
this.Path = fileName;
this.Dictionary = new Dictionary<string, object>();
this.Populate();
}
~Configuration()
{
if (this.Dictionary != null)
{
this.Dictionary.Clear();
}
}
private void Populate()
{
using (StreamReader reader = new StreamReader(this.Path))
{
string line;
string currentSection = string.Empty;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
if (line.StartsWith("[") && line.EndsWith("]"))
{
currentSection = line.Trim('[', ']');
}
else if (line.Contains("="))
{
this.Dictionary.Add(string.Format("{0}{1}{2}",
currentSection,
(currentSection != string.Empty) ? "_" : string.Empty,
line.Split('=')[0].Trim()),
ParseValue(line.Split('=')[1].Trim().Split(';')[0]));
}
}
}
}
private object ParseValue(string value)
{
if (string.IsNullOrWhiteSpace(value))
return string.Empty;
if (value.StartsWith("\"") && value.EndsWith("\""))
return value.Substring(1, value.Length - 1);
if (IsNumeric(value))
return Convert.ToInt32(value);
// TODO: FIXME Floating values (not to be confuse with IPAddress).
//if (IsFloating(value))
// return Convert.ToDouble(value);
if (string.Compare(value, "true", StringComparison.OrdinalIgnoreCase) == 0)
return true;
if (string.Compare(value, "false", StringComparison.OrdinalIgnoreCase) == 0)
return false;
return value;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (this.Dictionary.ContainsKey(binder.Name))
{
if (this.Dictionary[binder.Name] == null)
{
result = null;
}
else
{
result = this.Dictionary[binder.Name];
}
return true;
}
throw new ConfigurationException(binder.Name);
}
public override string ToString()
{
string result = this.Path + " [ ";
int processed = 0;
foreach (KeyValuePair<string, object> value in this.Dictionary)
{
result += value.Key;
processed++;
if (processed < this.Dictionary.Count)
{
result += ", ";
}
}
result += " ]";
return result;
}
private static bool IsNumeric(string value)
{
foreach (char c in value)
if (!char.IsDigit(c))
return false;
return true;
}
private static bool IsFloating(string value)
{
foreach (char c in value)
if (!char.IsDigit(c) && c != '.')
return false;
return true;
}
private class NullObject { }
}
Everything is working flawlessly. I've overriden TryGetMember and I'm returning the object based on the GetMemberBinder Name property. However, I'm facing a problem.
When I execute the following code:
string s = "some_config_key";
string d = configuration.s;
It retrieves the value of the key s rather than some_config_key, meaning that it doesn't evalute the value of the variable s. Is there a workaround for this?
Thanks.
C# dynamic features are partially compiled like the property/method access. To understand this lets take an example.
dynamic myVar = new { a=1, b="test" };
myVar.a += 1;
Here the C# type system would not test for the validity of the property while compilation, only at runtime the code is executed and if the property is not found it will through you runtime error.
So in your code the property access is already complied to access the property named "s" on the dynamic object "configuration".
In C# no-way you can do this not even in dynamically typed languages like python or javascript.
You have to use like this.
dynamic configuration = getConfig("path/to/file");
var myobj = configuration as IDictionary<string,object>;
string s = "config_key";
var value = myobj[s]; // this is correct.

Should C# have a lazy key word

Should C# have a lazy keyword to make lazy initialization easier?
E.g.
public lazy string LazyInitializeString = GetStringFromDatabase();
instead of
private string _backingField;
public string LazyInitializeString
{
get
{
if (_backingField == null)
_backingField = GetStringFromDatabase();
return _backingField;
}
}
I don't know about a keyword but it now has a System.Lazy<T> type.
It is officially part of .Net Framework 4.0.
It allows lazy loading of a value for a member.
It supports a lambda expression or a method to provide a value.
Example:
public class ClassWithLazyMember
{
Lazy<String> lazySource;
public String LazyValue
{
get
{
if (lazySource == null)
{
lazySource = new Lazy<String>(GetStringFromDatabase);
// Same as lazySource = new Lazy<String>(() => "Hello, Lazy World!");
// or lazySource = new Lazy<String>(() => GetStringFromDatabase());
}
return lazySource.Value;
}
}
public String GetStringFromDatabase()
{
return "Hello, Lazy World!";
}
}
Test:
var obj = new ClassWithLazyMember();
MessageBox.Show(obj.LazyValue); // Calls GetStringFromDatabase()
MessageBox.Show(obj.LazyValue); // Does not call GetStringFromDatabase()
In above Test code, GetStringFromDatabase() gets called only once. I think that is exactly what you want.
Edit:
After having comments from #dthorpe and #Joe, all I can say is following is the shortest it can be:
public class ClassWithLazyMember
{
Lazy<String> lazySource;
public String LazyValue { get { return lazySource.Value; } }
public ClassWithLazyMember()
{
lazySource = new Lazy<String>(GetStringFromDatabase);
}
public String GetStringFromDatabase()
{
return "Hello, Lazy World!";
}
}
Because following does not compile:
public Lazy<String> LazyInitializeString = new Lazy<String>(() =>
{
return GetStringFromDatabase();
});
And that property is type of Lazy<String> not String. You you always need to access it's value using LazyInitializeString.Value.
And, I am open for suggestions on how to make it shorter.
Have you considered using System.Lazy<T>?
public Lazy<String> LazyInitializeString = new Lazy<String>(() =>
{
return GetStringFromDatabase();
});
(This does have the disadvantage that you need to use LazyInitializeString.Value instead of just LazyInitializeString.)
Okay, you say in a comment that Lazy<T> won't suffice for you because it's readonly and you have to call .Value on it.
Still, it's clear that we want something along those lines - we already have a syntax for describing an action that is to be called, but not called immediately (indeed we have three; lambda, delegate creation and bare method name as a shortcut to the latter - the last thing we need is a fourth).
But we can quickly put together something that does that.
public enum SettableLazyThreadSafetyMode // a copy of LazyThreadSafetyMode - just use that if you only care for .NET4.0
{
None,
PublicationOnly,
ExecutionAndPublication
}
public class SettableLazy<T>
{
private T _value;
private volatile bool _isCreated;
private readonly Func<T> _factory;
private readonly object _lock;
private readonly SettableLazyThreadSafetyMode _mode;
public SettableLazy(T value, Func<T> factory, SettableLazyThreadSafetyMode mode)
{
if(null == factory)
throw new ArgumentNullException("factory");
if(!Enum.IsDefined(typeof(SettableLazyThreadSafetyMode), mode))
throw new ArgumentOutOfRangeException("mode");
_lock = (_mode = mode) == SettableLazyThreadSafetyMode.None ? null : new object();
_value = value;
_factory = factory;
_isCreated = true;
}
public SettableLazy(Func<T> factory, SettableLazyThreadSafetyMode mode)
:this(default(T), factory, mode)
{
_isCreated = false;
}
public SettableLazy(T value, SettableLazyThreadSafetyMode mode)
:this(value, () => Activator.CreateInstance<T>(), mode){}
public T Value
{
get
{
if(!_isCreated)
switch(_mode)
{
case SettableLazyThreadSafetyMode.None:
_value = _factory.Invoke();
_isCreated = true;
break;
case SettableLazyThreadSafetyMode.PublicationOnly:
T value = _factory.Invoke();
if(!_isCreated)
lock(_lock)
if(!_isCreated)
{
_value = value;
Thread.MemoryBarrier(); // ensure all writes involved in setting _value are flushed.
_isCreated = true;
}
break;
case SettableLazyThreadSafetyMode.ExecutionAndPublication:
lock(_lock)
{
if(!_isCreated)
{
_value = _factory.Invoke();
Thread.MemoryBarrier();
_isCreated = true;
}
}
break;
}
return _value;
}
set
{
if(_mode == SettableLazyThreadSafetyMode.None)
{
_value = value;
_isCreated = true;
}
else
lock(_lock)
{
_value = value;
Thread.MemoryBarrier();
_isCreated = true;
}
}
}
public void Reset()
{
if(_mode == SettableLazyThreadSafetyMode.None)
{
_value = default(T); // not strictly needed, but has impact if T is, or contains, large reference type and we really want GC to collect.
_isCreated = false;
}
else
lock(_lock) //likewise, we could skip all this and just do _isCreated = false, but memory pressure could be high in some cases
{
_value = default(T);
Thread.MemoryBarrier();
_isCreated = false;
}
}
public override string ToString()
{
return Value.ToString();
}
public static implicit operator T(SettableLazy<T> lazy)
{
return lazy.Value;
}
public static implicit operator SettableLazy<T>(T value)
{
return new SettableLazy<T>(value, SettableLazyThreadSafetyMode.ExecutionAndPublication);
}
}
Adding some more constructor overloads is left as an exercise to the reader :)
This will more than suffice:
private SettableLazy<string> _backingLazy = new SettableLazy<string>(GetStringFromDatabase);
public string LazyInitializeString
{
get
{
return _backingLazy;
}
set
{
_backingLazy = value;
}
}
Personally, I'm not overjoyed at the implicit operators, but they do show your requirements can be met. Certainly no need for another language feature.

Categories