81 votes

Est-il possible de définir une propriété une seule fois en C #?

Je cherche un moyen de permettre à une propriété dans un objet C # d'être définie une seule fois. Il est facile d'écrire le code pour le faire, mais je préférerais utiliser un mécanisme standard, le cas échéant.

public OneShot <int> SetOnceProperty {get; ensemble; }

Ce que je veux, c'est que la propriété puisse être définie si elle ne l'est pas déjà, mais émettra une exception si elle a été définie auparavant. Cela devrait fonctionner comme une valeur Nullable où je peux vérifier si elle a été définie ou non.

62voto

Marc Gravell Points 482669

Il existe un support direct pour cela dans la TPL dans .NET 4.0; jusque-là, vérifie toi-même ... ce n'est pas beaucoup de lignes, d'après ce que je me souviens ...

quelque chose comme:

 public sealed class WriteOnce<T>
{
    private T value;
    private bool hasValue;
    public override string ToString()
    {
        return hasValue ? Convert.ToString(value) : "";
    }
    public T Value
    {
        get
        {
            if (!hasValue) throw new InvalidOperationException("Value not set");
            return value;
        }
        set
        {
            if (hasValue) throw new InvalidOperationException("Value already set");
            this.value = value;
            this.hasValue = true;
        }
    }
    public T ValueOrDefault { get { return value; } }

    public static implicit operator T(WriteOnce<T> value) { return value.Value; }
}
 

Ensuite, utilisez, par exemple:

 readonly WriteOnce<string> name = new WriteOnce<string>();
public WriteOnce<string> Name { get { return name; } }
 

33voto

Michael Meadows Points 15277

Vous pouvez lancer le vôtre (voir la fin de la réponse pour une implémentation plus robuste, thread-safe et prenant en charge les valeurs par défaut).

 public class SetOnce<T>
{
    private bool set;
    private T value;

    public T Value
    {
        get { return value; }
        set
        {
            if (set) throw new AlreadySetException(value);
            set = true;
            this.value = value;
        }
    }

    public static implicit operator T(SetOnce<T> toConvert)
    {
        return toConvert.value;
    }
}
 

Vous pouvez l'utiliser comme ceci:

 public class Foo
{
    private readonly SetOnce<int> toBeSetOnce = new SetOnce<int>();

    public int ToBeSetOnce
    {
        get { return toBeSetOnce; }
        set { toBeSetOnce.Value = value; }
    }
}
 

Implémentation plus robuste ci-dessous

 public class SetOnce<T>
{
    private readonly object syncLock = new object();
    private readonly bool throwIfNotSet;
    private readonly string valueName;
    private bool set;
    private T value;

    public SetOnce(string valueName)
    {
        this.valueName = valueName;
        throwIfGet = true;
    }

    public SetOnce(string valueName, T defaultValue)
    {
        this.valueName = valueName;
        value = defaultValue;
    }

    public T Value
    {
        get
        {
            lock (syncLock)
            {
                if (!set && throwIfNotSet) throw new ValueNotSetException(valueName);
                return value;
            }
        }
        set
        {
            lock (syncLock)
            {
                if (set) throw new AlreadySetException(valueName, value);
                set = true;
                this.value = value;
            }
        }
    }

    public static implicit operator T(SetOnce<T> toConvert)
    {
        return toConvert.value;
    }
}


public class NamedValueException : InvalidOperationException
{
    private readonly string valueName;

    public NamedValueException(string valueName, string messageFormat)
        : base(string.Format(messageFormat, valueName))
    {
        this.valueName = valueName;
    }

    public string ValueName
    {
        get { return valueName; }
    }
}

public class AlreadySetException : NamedValueException
{
    private const string MESSAGE = "The value \"{0}\" has already been set.";

    public AlreadySetException(string valueName)
        : base(valueName, MESSAGE)
    {
    }
}

public class ValueNotSetException : NamedValueException
{
    private const string MESSAGE = "The value \"{0}\" has not yet been set.";

    public ValueNotSetException(string valueName)
        : base(valueName, MESSAGE)
    {
    }
}
 

11voto

Anton Gogolev Points 59794

Cela peut être fait soit en jouant avec le drapeau:

 private OneShot<int> setOnce;
private bool setOnceSet;

public OneShot<int> SetOnce
{
    get { return setOnce; }
    set
    {
        if(setOnceSet)
            throw new InvalidOperationException();

        setOnce = value;
        setOnceSet = true;
    }
}
 

ce qui n'est pas bon car vous pouvez potentiellement recevoir une erreur d'exécution. Mieux vaut appliquer ce comportement à la compilation:

 public class Foo
{
    private readonly OneShot<int> setOnce;        

    public OneShot<int> SetOnce
    {
        get { return setOnce; }
    }

    public Foo() :
        this(null)
    {
    }

    public Foo(OneShot<int> setOnce)
    {
        this.setOnce = setOnce;
    }
 

}

puis utilisez l'un ou l'autre constructeur.

5voto

Vizu Points 1141

Aucune fonctionnalité de ce type dans C # (à partir de la version 3.5). Vous devez le coder vous-même.

4voto

JaredPar Points 333733

Comme Marc l'a dit, il n'y a aucun moyen de le faire par défaut dans .Net, mais en ajouter un vous-même n'est pas trop difficile.

 public class SetOnceValue<T> { 
  private T m_value;
  private bool m_isSet;
  public bool IsSet { get { return m_isSet; }}
  public T Value { get {
    if ( !IsSet ) {
       throw new InvalidOperationException("Value not set");
    }
    return m_value;
  }
  public T ValueOrDefault { get { return m_isSet ? m_value : default(T); }}
  public SetOnceValue() { }
  public void SetValue(T value) {
    if ( IsSet ) {
      throw new InvalidOperationException("Already set");
    }
    m_value = value;
    m_isSet = true;
  }
}
 

Vous pouvez ensuite l'utiliser comme support pour votre propriété particulière.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X