web-dev-qa-db-fra.com

Propriétés automatiques C # Lazy Loaded

En C #,

Existe-t-il un moyen de transformer une propriété automatique en propriété automatique chargée paresseuse avec une valeur par défaut spécifiée?

Essentiellement, j'essaie de transformer ceci ...

private string _SomeVariable

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

          return _SomeVariable;
     }
}

dans quelque chose de différent, où je peux spécifier la valeur par défaut et gérer le reste automatiquement ...

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

Non, il n'y en a pas. Les propriétés implémentées automatiquement ne fonctionnent que pour implémenter les propriétés les plus élémentaires: champ de sauvegarde avec getter et setter. Il ne supporte pas ce type de personnalisation. 

Cependant, vous pouvez utiliser le type 4.0 Lazy<T> pour créer ce modèle.

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

Ce code calculera par la suite la valeur de _someVariable lors du premier appel de l'expression Value. Elle ne sera calculée qu'une fois et mettra en cache la valeur pour les utilisations futures de la propriété Value

93
JaredPar

Le plus concis possible est probablement d’utiliser l’opérateur null-coalescing:

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

Il existe une nouvelle fonctionnalité dans C # 6 appelée Expression Bodied Auto-Properties , qui vous permet de l'écrire un peu plus clairement:

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

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

Peut maintenant être écrit comme:

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

   public string SomeVariable => _someVariable.Value;
}
12
Alexander Derck

Pas comme ça, les paramètres pour les attributs doivent avoir une valeur constante, vous ne pouvez pas appeler du code (même du code statique).

Cependant, vous pourrez peut-être implémenter quelque chose avec Aspects de PostSharp.

Vérifie-les:

PostSharp

5
Aren

Voici ma mise en œuvre d'une solution à votre problème. Fondamentalement, l'idée est une propriété qui sera définie par une fonction lors du premier accès et les accès suivants produiront la même valeur de retour que le premier. 

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

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

Puis utiliser:

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

Il y a bien sûr le temps système nécessaire pour faire passer le pointeur de fonction, mais cela me convient et je ne remarque pas trop de temps système par rapport à l'exécution répétée de la méthode. 

5
deepee1

Je suis un grand fan de cette idée et voudrais proposer l'extrait C # suivant que j'ai appelé proplazy.snippet (vous pouvez l'importer ou le coller dans le dossier standard que vous pouvez obtenir à partir du Gestionnaire d'extraits)

Voici un exemple de sa sortie:

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

Voici le contenu du fichier d'extrait: (enregistrez sous 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>
2
Zephryl

Je ne pense pas que cela soit possible avec du C # pur. Mais vous pouvez le faire en utilisant un enregistreur IL comme PostSharp . Par exemple, cela vous permet d'ajouter des gestionnaires avant et après des fonctions en fonction d'attributs.

2
CodesInChaos

Je l'ai fait comme ça:

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;
    }
}

et plus tard, vous pouvez l'utiliser comme 

       public virtual bool SomeProperty => this.LazyValue(() =>
    {
        return true; 
    });
0
Alexander Zuban
[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;
}

et j'appelle comme ci-dessous

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

Si vous utilisez un constructeur lors de l'initialisation différée, les extensions suivantes peuvent également être utiles.

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>()); */
0
Makeman

https://github.com/bcuff/AutoLazy utilise Fody pour vous donner un résultat similaire 

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);
        }
    }
}
0
Sam