Soru Sadece C # biriminde bir özellik belirlemenin bir yolu var mı


C # nesnesindeki bir özelliğin yalnızca bir kez ayarlanmasına izin vermenin bir yolunu arıyorum. Bunu yapmak için kodu yazmak kolaydır, ancak varsa standart bir mekanizmayı kullanmayı tercih ederim.

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

Olmak istediğim, özellik önceden ayarlanmamışsa, ancak önceden ayarlanmışsa bir istisna atılmasıdır. Ayarlanıp ayarlanmadığını kontrol edebildiğim Nullable bir değer gibi çalışmalı.


54
2018-05-08 13:27


Menşei


Bu benim için kokuyor, üzgünüm. Neden değeri bir kurucuda geçirmiyorsunuz? Ayrıca, ayarlanmadan emin olmak için değeri ayarlamadan önce kontrol edebilmek için arayana geri besleme sağlayacak mısınız? - Binary Worrier
İdeal olarak bunu yapıcıya aktarırdım, ama nesneyi bir süre içinde inşa etmem gerekiyor. Örneğin, bir kayıt A bilgi sağlar, bir sonraki kayıt B ve C bilgi sağlar. Tam bir bilgi setine sahip olduğumda, bu bilgileri tüm kayıtları tekrar birbirine bağlamak için kullanırım. Doğrulamak için bir çalışma zamanı mekanizması istedim, sadece değerleri bir kez ayarladım, onları psuedo'yu hazırladım! - Nick Randell
Ve evet - benim için de kokuyor! - Nick Randell
Sadece kokuyor, çünkü koktuğunu varsayıyorsun. Bunu, benim başlattığım bir nesneyi çağrıştığında ve bağımlılıkları enjekte etmem için bana hiçbir yol göstermediğinde ihtiyacım vardı. Bu yüzden, kompozisyon köküm, kapsayıcıyı, bu nesne üzerinde tanımlanan bir kez statik özelliğe yazdı. - Ronnie Overby


Cevaplar:


NET 4.0'da TPL'de bunun için doğrudan destek var;

(düzenleme: Yukarıdaki cümle beklentisiyle yazılmıştır.) System.Threading.WriteOnce<T> O anda mevcut olan "önizleme" bitlerinde mevcuttu, fakat bu TPL RTM / GA isabeti öncesinde buharlaşır gibi görünüyor)

O zamana kadar sadece kendiniz kontrol edin ... hatırladığımdan çok satır değil ...

gibi bir şey:

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

Sonra kullanın, örneğin:

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

43
2018-05-08 13:29



Kendi cevabımı parlatmakla o kadar meşguldüm ki yazdığın aynı şeyleri yazdığını farketmemiştim. Artık kendimi çok özel hissetmiyorum. - Michael Meadows
Bunun doğrudan destek versiyonunu eklemeyi düşünür müsünüz (zaten .NET 4.0'dan geçmişiz). Bu özellik ile ilgili herhangi bir bilgi bulamıyorum. - Destrictor
Marc'ın hakkında konuşması mümkün msdn.microsoft.com/ru-ru/library/hh194820(v=vs.110).aspx - Dmitry Dovgopoly
@MarcGravell TPL'de tam olarak ne var? TaskCompletionSource<T>? - Ronnie Overby
Paralel uzantılar çerçevesinin orijinal CTP'sinde @ ArturoTorresSánchez (bunu yazdığım zamanki "geçerli" idi, IIRC) WriteOnce<T> türü - değil veri akışı bitleriyle ilgili. Bu hala var mı, yoksa RTM'ye mi yapıldığından emin değil: fakat - yazı yazıldığı zaman vardı. Onların izlerini hala bulabilirsiniz - İşte, İşte - Marc Gravell♦


Kendinizi yuvarlayabilirsiniz (iş parçacığı güvenli ve varsayılan değerleri destekleyen daha sağlam bir uygulama için yanıtın sonuna bakın).

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

Bunu şöyle kullanabilirsiniz:

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

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

Aşağıdaki daha güçlü uygulama

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)
    {
    }
}

27
2018-05-08 13:32



Değeri ayarladığınızda yalnızca this.set değerini true değerine ayarlamıyorsunuz; - Tomas Lycken
Şimdi gidiyoruz, onu doğruya ayarlıyoruz - JoshBerke
İplik güvenliği için +1. - Marc Gravell♦
throwIfGet (ilk kurucuda) herhangi bir yerde tanımlanmamış - reggaeguitar
Bir mülkün kilitlenmesini bekliyorum. - Chris Marisic


Bu, bayrakla uğraşmakla yapılabilir:

private OneShot<int> setOnce;
private bool setOnceSet;

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

        setOnce = value;
        setOnceSet = true;
    }
}

Bu, iyi bir çalışma zamanı hatası alabileceğiniz için iyi değil. Bu davranışı derleme zamanında uygulamak daha iyidir:

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

ve sonra ya kurucu kullanın.


11
2018-05-08 13:33



İki kez ayarlamaya çalışırsam, bir çalışma zamanı hatası olmasını istiyorum. Aksi takdirde, salt okunur üye değişkenini ayarlayan bir kurucu oluşturmuş olurdum. - Nick Randell
Kurucudaki değeri ayarlamak için +1 - Scoregraphic


Marc'ın varsayılan olarak bunu Net ile yapmanın bir yolu olmadığını söyledim ama bir tane eklemek çok zor değil.

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

Daha sonra bunu özel mülkünüz için destek olarak kullanabilirsiniz.


4
2018-05-08 13:34





C # 'de böyle bir özellik yok (3.5' den itibaren). Kendin kodlamak zorundasın.


3
2018-05-08 13:32



Microsoft böyle bir özellik sağlamalıdır. - Syed Tayyab Ali
Emin değilim. Bir şey bir kez ayarlanmışsa, muhtemelen bir yapıcı parametresi olmalıdır. - Vizu


Okumayı düşündün mü? http://en.csharp-online.net/const,_static_and_readonly

Yalnızca init sırasında ayarlanabilir, ancak aradığınız şey olabilir.


2
2018-01-03 18:51



readonly özellikleri için mevcut değildir - John


/// <summary>
/// Wrapper for once inizialization
/// </summary>
public class WriteOnce<T>
{
    private T _value;
    private Int32 _hasValue;

    public T Value
    {
        get { return _value; }
        set
        {
            if (Interlocked.CompareExchange(ref _hasValue, 1, 0) == 0)
                _value = value;
            else
                throw new Exception(String.Format("You can't inizialize class instance {0} twice", typeof(WriteOnce<T>)));
        }
    }

    public WriteOnce(T defaultValue)
    {
        _value = defaultValue;
    }

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

1
2017-10-02 14:18





İşte bunu benim almam:

public class ReadOnly<T> // or WriteOnce<T> or whatever name floats your boat
{
    private readonly TaskCompletionSource<T> _tcs = new TaskCompletionSource<T>();

    public Task<T> ValueAsync => _tcs.Task;
    public T Value => _tcs.Task.Result;

    public bool TrySetInitialValue(T value)
    {
        try
        {
            _tcs.SetResult(value);
            return true;
        }
        catch (InvalidOperationException)
        {
            return false;
        }
    }

    public void SetInitialValue(T value)
    {
        if (!TrySetInitialValue(value))
            throw new InvalidOperationException("The value has already been set.");
    }

    public static implicit operator T(ReadOnly<T> readOnly) => readOnly.Value;
    public static implicit operator Task<T>(ReadOnly<T> readOnly) => readOnly.ValueAsync;
}

Marc'ın cevabı TPL'nin bu işlevselliği sağladığını ve düşünmek  TaskCompletionSource<T> kastettiği şey olabilirdi, ama emin olamam.

Çözümümün bazı güzel özellikleri:

  • TaskCompletionSource<T> Uygulamayı basitleştiren resmi olarak destekleyici bir MS sınıfıdır.
  • Senkronize veya senkronize olmayan değeri almayı seçebilirsiniz.
  • Bu sınıfın bir örneği, sakladığı değerin türüne dolaylı olarak dönüşecektir. Bu, değeri geçmeniz gerektiğinde kodunuzu biraz toplayabilir.

1
2017-10-24 00:33





Bunu yapabilirsiniz ama net bir çözüm değildir ve kod okunabilirliği en iyi değildir. Kod tasarımı yapıyorsanız, bir göz atabilirsiniz. singleton AOP ile birlikte gerçekleşmesi yolunu kesmek belirleyiciler. Gerçekleşme sadece 123'tür :)


0
2018-05-08 13:49





interface IFoo {

    int Bar { get; }
}

class Foo : IFoo {

    public int Bar { get; set; }
}

class Program {

    public static void Main() {

        IFoo myFoo = new Foo() {
            Bar = 5 // valid
        };

        int five = myFoo.Bar; // valid

        myFoo.Bar = 6; // compilation error
    }
}

MyFoo'nun bir IFoo olarak ilan edildiğine, ancak bir Foo olarak başlatıldığına dikkat edin.

Bu, Bar'ın başlatıcı bloğu içinde ayarlanabileceği, ancak daha sonra myFoo'ya atıfta bulunmayacağı anlamına gelir.


0
2017-09-23 18:32





Cevaplar, gelecekte bir nesneye referans alan nesnelerin onu değiştirmeyi denemeyeceğini varsayar. Buna karşı korumak istiyorsanız, bir kez yazdığınız kodunuzu yalnızca ICONeable veya ilkel uygulayan türler için çalışmanız gerekir. Dize türü, örneğin ICONeable uygular. Daha sonra, gerçek veriler yerine, verilerin bir klonunu veya ilkel öğenin yeni örneğini döndürürsünüz.

Sadece temel ürünler için jenerikler:     T GetObject nerede T: struct;

Verilere referans veren nesnelerin bunun üzerine yazılmayacağını biliyorsanız, buna gerek yoktur.

Ayrıca, ReadOnlyCollection uygulamanız için çalışıp çalışmadığını da göz önünde bulundurun. Verilerde bir değişiklik yapıldığında bir istisna atılır.


0
2017-11-13 14:51