Değişmez alanlardaki ayarlayıcıları nasıl kullanabilirim?


25

İki readonly intalanlı bir sınıfım var . Özellikler olarak ortaya çıkarlar:

public class Thing
{
    private readonly int _foo, _bar;

    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public int Foo { get { return _foo; } set { } }

    public int Bar { get { return _bar; } set { } }
}

Ancak, bu, aşağıdakilerin tamamen yasal bir kod olduğu anlamına gelir:

Thing iThoughtThisWasMutable = new Thing(1, 42);

iThoughtThisWasMutable.Foo = 99;  // <-- Poor, mistaken developer.
                                  //     He surely has bugs now. :-(

Bunun işe yarayacağını varsaymaktan kaynaklanan hataların sinsi olacağından emin olabilirsiniz. Elbette, hatalı geliştirici dokümanları okumuş olmalıydı. Ancak bu, derleme veya çalışma zamanı hatasının bir sorun hakkında onu uyarmaması gerçeğini değiştirmez.

Nasıl olmalıdır Thingsınıf Devs yukarıdaki hata yapma olasılığı daha düşük olacak şekilde değiştirilebilir?

Bir istisna atmak? Bir özellik yerine bir alıcı yöntemi kullanın?



1
Teşekkürler, @gnat. Bu yazı (hem soru hem de cevap) Kapital I'de olduğu gibi arayüzlerden bahsediyor gibi görünüyor Interfaces. Ne yaptığımdan emin değilim.
kdbanman

6
@gnat, bu korkunç bir tavsiye. Arayüzler, hala test edilmesi kolay olan halka açık değişken VO / DTO'ları sunmanın harika bir yolunu sunar.
David Arno,

4
@gnat, yaptım ve soru, OP'nin arayüzlerin saçma sapanlığı, yani saçmalıkları yok edeceğini düşündüğü gibi bir anlam ifade etmiyor.
David Arno,

1
@gnat, bu soru DTO'lar için arayüz ilan etmekle ilgiliydi. En çok oylanan cevap, arayüzlerin zararlı olamayacağı, ancak muhtemelen gereksiz olacağıydı.
Paul Draper

Yanıtlar:


107

Bu kodu neden yasal yapıyorsun?
Çıkarıp set { }hiçbir şey yaparsa.
Salt okunur bir genel özelliği şu şekilde tanımlayabilirsiniz:

public int Foo { get { return _foo; } }

3
Bunun bir nedenden dolayı yasal olduğunu düşünmedim, ama mükemmel çalışıyor gibi görünüyor! Mükemmel, teşekkürler.
kdbanman

1
@kdbanman Muhtemelen IDE'nin bir noktada yaptığını gördüğünüz bir şeyle bir ilgisi vardır - muhtemelen otomatik tamamlama.
Panzercrisis,

2
@kdbanman; yasal olmayan (C # 5 e kadar) şudur public int Foo { get; }(yalnızca bir alıcı ile otomatik mülkiyet) public int Foo { get; private set; }.
Laurent LA RIZZA

@LaurentLARIZZA, hafızamı dürtdün. Bu tam olarak kafamın karıştığı yerdi.
kdbanman

45

C # 5 ve öncesinde, bir alıcı tarafından maruz bırakılan değişmez alanlar için iki seçenekle karşı karşıya kaldık:

  1. Salt okunur bir yedekleme değişkeni oluşturun ve bunu manuel bir alıcı aracılığıyla döndürün. Bu seçenek güvenlidir ( readonlydeğişmezliği imha etmek için açıkça açıkça kaldırılması gerekir . Yine de birçok kazan plakası kodu oluşturuldu).
  2. Özel bir ayarlayıcı ile bir otomatik özellik kullanın. Bu, daha basit kod oluşturur, ancak değişmezliği yanlışlıkla kırmak daha kolaydır.

Yine de C # 6 ile (dün piyasaya sürülen VS2015'te mevcut), şimdi her iki dünyanın da en iyisini elde ediyoruz: salt okunur otomatik özellikler. Bu OP'nin sınıfını basitleştirmemizi sağlar:

public class Thing
{
    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; }
    public int Bar { get; }
}

Foo = fooVe Bar = barsatırlar yerine (daha basit kod başarır olan) açıkça tanımlanmalıdır zorunda kalmak yerine, ima (sağlam salt okunur gereksinimi başarır) yapıcı ve destek alanında geçerlidir.


2
Ve birincil yapıcılar, yapıcının sözdizimsel yükünden kurtularak daha da basitleştirebilirler.
Sebastian Redl


1
@DanLyons Lanet olsun, bunu kod tabanımız boyunca kullanmayı dört gözle bekliyordum.
Sebastian Redl

12

Sadece ayarlayıcılardan kurtulabilirsin. Hiçbir şey yapmazlar, kullanıcıları şaşırtırlar ve hatalara yol açarlar. Ancak, bunun yerine onları özel kılabilir ve böylece kodunuzu basitleştirerek destek değişkenlerinden kurtulabilirsiniz:

public class Thing
{
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; private set; }

    public int Bar { get; private set; }
}

1
" public int Foo { get; private set; }" Bu sınıf artık değişemez çünkü kendi kendini değiştirebilir. Yine de teşekkürler!
kdbanman

4
Açıklanan sınıf, kendini değiştiremediğinden değişmezdir.
David Arno,

1
Gerçek dersim, verdiğim MWE'den biraz daha karmaşık . Ben gerçek değişmezliğin peşindeyim , iplik güvenliği ve sınıf içi garantilerle birlikte.
kdbanman

4
@kdbanman, demek istediğin? Sınıfınız inşaat sırasında yalnızca özel ayarlayıcılara yazarsa, o zaman "gerçek değişmezliği" uygulamıştır. readonlyAnahtar kelime, sadece poka-yoke olduğu gelecekte immutablity kırma sınıfına karşı korur yani; Ancak değişmezliği sağlamak için gerekli değildir.
David Arno

3
İlginç bir terim, google yapmak zorunda kaldım - poka-yoke "hataya dayanıklılık" anlamına gelen Japonca bir terimdir. Haklısın! Ben de aynen böyle yapıyorum.
kdbanman

6

İki çözüm.

Basit:

Değiştirilebilir salt okunur nesneler için David tarafından belirtilen ayarlayıcıları dahil etmeyin.

Alternatif:

Ayarlayıcıların öncekine göre daha ayrıntılı olan yeni bir değişmez nesne döndürmelerine izin verin, ancak başlatılan her nesne için zaman içinde durum sağlar. Bu tasarım, tüm zorunlu OOP dillerine yayılan İplik güvenliği ve değişmezliği için çok faydalı bir araçtır.

pseudocode

public class Thing
{

{readonly vars}

    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

kamu statik fabrikası

public class Thing
{

{readonly vars}

    private Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public static with(int foo, int bar) {
        return new Thing(foo, bar)
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

2
setXYZ, fabrika yöntemleri için korkunç isimlerdir, XYZ veya benzerleriyle birlikte düşünün.
Ben Voigt

Teşekkürler, faydalı yorumunuzu yansıtacak şekilde cevabımı güncelle :-)
Matt Smithies

Soru özellikle belirleyici yöntemleri değil mülk belirleyicileri sormaktı.
user253751

Doğru, ama OP bir kesilebilir durumu hakkında endişeli olası gelecek geliştirici. Özel salt okunur veya özel son anahtar kelimeler kullanan değişkenler, kendileri belgelenmelidir, bu anlaşılmadığı takdirde orijinal hata değildir. Durumu değiştirmek için farklı yöntemler anlamak çok önemlidir. Çok yardımcı olan başka bir yaklaşım ekledim. Kod dünyasında aşırı paranoyaya neden olan pek çok şey var, özel tek tek varlıklar onlardan biri olmamalı.
Matt Smithies
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.