C# Лениво Загруженные Автоматические Свойства


В C#

есть ли способ превратить автоматическое свойство в ленивое загруженное автоматическое свойство с указанным значением по умолчанию?

по сути, я пытаюсь превратить это...

private string _SomeVariable

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

          return _SomeVariable;
     }
}

во что-то другое, где я могу указать значение по умолчанию, и он обрабатывает остальные автоматически...

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

9 ответов:

нет нет нет. Авто-реализованные свойства только функция для реализации самых основных свойств: резервное поле с геттером и сеттером. Он не поддерживает эту функцию.

однако вы можете использовать 4.0 Lazy<T> тип создать этот шаблон

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

этот код будет лениво вычислять значение _someVariable в первый раз Value выражение называется. Он будет рассчитан только один раз и будет кэшировать значение для будущего использование Value свойства

вероятно, самое краткое, что вы можете получить, это использовать оператор null-coalescing:

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

В C#6 появилась новая функция под названием Выражение Bodied Auto-Properties, что позволяет написать его немного чище:

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

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

теперь можно записать так:

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

   public string SomeVariable => _someVariable.Value;
}

Не так, параметры для атрибутов должны быть постоянными по значению, вы не можете вызвать код (даже статический код).

однако вы можете реализовать что-то с аспектами PostSharp.

вот они:

PostSharp

вот моя реализация решения вашей проблемы. В основном идея-это свойство, которое будет установлено функцией при первом доступе, а последующие обращения будут давать то же возвращаемое значение, что и первое.

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

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

затем использовать:

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

есть, конечно, накладные расходы на передачу указателя функции вокруг, но это делает работу для меня, и я не замечаю слишком много накладных расходов по сравнению с запуском метода снова и снова.

Я не думаю, что это возможно с чистым C#. Но вы могли бы сделать это с помощью IL rewriter, как PostSharp. Например, он позволяет добавлять обработчики до и после функций в зависимости от атрибутов.

Я большой поклонник этой идеи, и хотел бы предложить следующий фрагмент C#, который я назвал proplazy.фрагмент.(вы можете либо импортировать это, либо вставить его в стандартную папку, которую вы можете получить из диспетчера фрагментов)

вот пример ее вывода:

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

вот содержимое файла сниппета: (сохранить как proplazy.сниппет)

<?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>

https://github.com/bcuff/AutoLazy использует Фоди, чтобы дать вам что-то вроде этого

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);
        }
    }
}
[Serializable]
public class RaporImza
{
    private readonly Func<ReportConfig> _getReportLayout;
    public RaporImza(Func<ReportConfig> getReportLayout)
    {
        _getReportLayout = getReportLayout;
    }

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

    public string ImzaAtanKisiAdi => GetReportLayoutResult.ReportSignatureName;

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

и я звоню, как ниже

result.RaporBilgisi = new ExchangeProgramPersonAllDataModel.RaporImza(() => _reportConfigService.GetReportLayout(documentTypeId));