C# Lazy Loaded Automatic Properties

C#Automatic Properties

C# Problem Overview


In C#,

Is there a way to turn an automatic property into a lazy loaded automatic property with a specified default value?

Essentially, I am trying to turn this...

private string _SomeVariable

public string SomeVariable
{
     get
     {
          if(_SomeVariable == null)
          {
             _SomeVariable = SomeClass.IOnlyWantToCallYouOnce();
          }

          return _SomeVariable;
     }
}

into something different, where I can specify the default and it handles the rest automatically...

[SetUsing(SomeClass.IOnlyWantToCallYouOnce())]
public string SomeVariable {get; private set;}

C# Solutions


Solution 1 - C#

No there is not. Auto-implemented properties only function to implement the most basic of properties: backing field with getter and setter. It doesn't support this type of customization.

However you can use the 4.0 Lazy<T> type to create this pattern

private Lazy<string> _someVariable =new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce);
public string SomeVariable => _someVariable.Value;

This code will lazily calculate the value of _someVariable the first time the Value expression is called. It will only be calculated once and will cache the value for future uses of the Value property

Solution 2 - C#

Probably the most concise you can get is to use the null-coalescing operator:

get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); }

Solution 3 - C#

There is a new feature in C#6 called Expression Bodied Auto-Properties, which allows you to write it a bit cleaner:

public class SomeClass
{ 
   private Lazy<string> _someVariable = new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce);

   public string SomeVariable 
   {
      get { return _someVariable.Value; }
   }
}

Can now be written as:

public class SomeClass
{
   private Lazy<string> _someVariable = new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce);

   public string SomeVariable => _someVariable.Value;
}


Solution 4 - C#

Operator ??= is available using C# 8.0 and later, so you can now do it even more concise:

private string _someVariable;

public string SomeVariable => _someVariable ??= SomeClass.IOnlyWantToCallYouOnce();

Solution 5 - C#

Not like that, parameters for attributes must be constant in value, you cannot call code (Even static code).

You may however be able to implement something with PostSharp's Aspects.

Check them out:

PostSharp

Solution 6 - C#

Here's my implementation of a solve to your problem. Basically the idea is a property that will be set by a function at first access and subsequent accesses will yield the same return value as the first.

public class LazyProperty<T>
{
    bool _initialized = false;
    T _result;

    public T Value(Func<T> fn)
    {
		if (!_initialized)
		{
			_result = fn();
			_initialized = true;
		}
		return _result;
    }
 }

Then to use:

LazyProperty<Color> _eyeColor = new LazyProperty<Color>();
public Color EyeColor
{ 
	get 
	{
		return _eyeColor.Value(() => SomeCPUHungryMethod());
	} 
}

There is of course the overhead of passing the function pointer around, but it does the job for me and I don't notice too much overhead compared to running the method over and over again.

Solution 7 - C#

I'm a big fan of this idea, and would like to offer up the following C# snippet which I called proplazy.snippet.(you can either import this or paste it into the standard folder which you can get from the Snippet Manager)

Here's a sample of its output:

private Lazy<int> myProperty = new Lazy<int>(()=>1);
public int MyProperty { get { return myProperty.Value; } }

Here's the snippet file contents: (save as proplazy.snippet)

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>proplazy</Title>
			<Shortcut>proplazy</Shortcut>
			<Description>Code snippet for property and backing field</Description>
			<Author>Microsoft Corporation</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>Property type</ToolTip>
					<Default>int</Default>
				</Literal>
				<Literal>
					<ID>field</ID>
					<ToolTip>The variable backing this property</ToolTip>
					<Default>myVar</Default>
				</Literal>
				<Literal>
					<ID>func</ID>
					<ToolTip>The function providing the lazy value</ToolTip>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>Property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
				
			</Declarations>
			<Code Language="csharp"><![CDATA[private Lazy<$type$> $field$ = new Lazy<$type$>($func$);
			public $type$ $property$ { get{ return $field$.Value; } }
			$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

Solution 8 - C#

I did it like this:

public static class LazyCachableGetter
{
    private static ConditionalWeakTable<object, IDictionary<string, object>> Instances = new ConditionalWeakTable<object, IDictionary<string, object>>();
    public static R LazyValue<T, R>(this T obj, Func<R> factory, [CallerMemberName] string prop = "")
    {
        R result = default(R);
        if (!ReferenceEquals(obj, null))
        {
            if (!Instances.TryGetValue(obj, out var cache))
            {
                cache = new ConcurrentDictionary<string, object>();
                Instances.Add(obj, cache);

            }


            if (!cache.TryGetValue(prop, out var cached))
            {
                cache[prop] = (result = factory());
            }
            else
            {
                result = (R)cached;
            }

        }
        return result;
    }
}

and later you can use it like

       public virtual bool SomeProperty => this.LazyValue(() =>
    {
        return true; 
    });

Solution 9 - C#

I don't think this is possible with pure C#. But you could do it using an IL rewriter like http://www.sharpcrafters.com/postsharp">PostSharp</a>;. For example it allows you to add handlers before and after functions depending on attributes.

Solution 10 - C#

https://github.com/bcuff/AutoLazy uses Fody to give you something like this

public class MyClass
{
	// This would work as a method, e.g. GetSettings(), as well.
	[Lazy]
	public static Settings Settings
	{
		get
		{
			using (var fs = File.Open("settings.xml", FileMode.Open))
			{
				var serializer = new XmlSerializer(typeof(Settings));
				return (Settings)serializer.Deserialize(fs);
			}
		}
	}

	[Lazy]
	public static Settings GetSettingsFile(string fileName)
	{
		using (var fs = File.Open(fileName, FileMode.Open))
		{
			var serializer = new XmlSerializer(typeof(Settings));
			return (Settings)serializer.Deserialize(fs);
		}
	}
}

Solution 11 - C#

[Serializable]
public class ReportModel
{
    private readonly Func<ReportConfig> _getReportLayout;
    public ReportModel(Func<ReportConfig> getReportLayout)
    {
        _getReportLayout = getReportLayout;
    }

    private ReportConfig _getReportLayoutResult;
    public ReportConfig GetReportLayoutResult => _getReportLayoutResult ?? (_getReportLayoutResult = _getReportLayout());


    public string ReportSignatureName => GetReportLayoutResult.ReportSignatureName;
    public string ReportSignatureTitle => GetReportLayoutResult.ReportSignatureTitle;
    public byte[] ReportSignature => GetReportLayoutResult.ReportSignature;
}

Solution 12 - C#

If you use a constructor during lazy initialization, following extensions may be helpful too

public static partial class New
{
	public static T Lazy<T>(ref T o) where T : class, new() => o ?? (o = new T());
	public static T Lazy<T>(ref T o, params object[] args) where T : class, new() =>
	    	o ?? (o = (T) Activator.CreateInstance(typeof(T), args));
}

Usage

	private Dictionary<string, object> _cache;

	public Dictionary<string, object> Cache => New.Lazy(ref _cache);

					/* _cache ?? (_cache = new Dictionary<string, object>()); */

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionctorxView Question on Stackoverflow
Solution 1 - C#JaredParView Answer on Stackoverflow
Solution 2 - C#Gabe MoothartView Answer on Stackoverflow
Solution 3 - C#Alexander DerckView Answer on Stackoverflow
Solution 4 - C#CarlosView Answer on Stackoverflow
Solution 5 - C#ArenView Answer on Stackoverflow
Solution 6 - C#deepee1View Answer on Stackoverflow
Solution 7 - C#ZephrylView Answer on Stackoverflow
Solution 8 - C#Alexander ZubanView Answer on Stackoverflow
Solution 9 - C#CodesInChaosView Answer on Stackoverflow
Solution 10 - C#SamView Answer on Stackoverflow
Solution 11 - C#murat_yuceerView Answer on Stackoverflow
Solution 12 - C#MakemanView Answer on Stackoverflow