2010-02-15 23 views
9

C# delegelerle geri alma işlevi uygulamak çalışıyorum. Temel olarak, her bir geri alma işlemini uygulayan delege listelerini tutan bir UndoStack var. Kullanıcı Düzenle: Geri Al'ı seçtiğinde, bu yığın ilk delege açılır ve onu çalıştırır. Her işlem, uygun bir geri alma temsilcisini yığına itmekten sorumludur. Sanırım "SetCashOnHand" adlı bir yöntemim var. ,C# Lambdas: Nasıl "Dereference" ertelemek için * değil *?

public void SetCashOnHand(int x) { 
    int coh = this.cashOnHand; 
    undo u =() => this.setCashOnHand(coh); 
    this.cashOnHand = x; 
    undoStack.Push(u); 
} 

Yani bu yöntem geri alma temsilci oluşturur operasyonu yapar ve (Başarılı varsayarak) UndoStack üzerine geri alma temsilci iter: Şuna benzer. (UndoStack, bir geri alma bağlamında undoStack.Push çağrılırsa yeterince akıllıdır; temsilci bunun yerine yineleme yığınının üzerine gider.)

Sorunum, bu .cashOnHand'i "önbelleğe almak" için biraz can sıkıcı olması. coh değişkeni. Ben sadece bunu yazmak isterdim:

undo u =() => this.setCashOnHand(this.cashOnHand); 

Ama cashOnHand ait mevcut değerini almazsınız elbette

; Delegeye çağrılana kadar değeri ararken, kod hiçbir şey yapmadan rüzgarlar. Değeri, coh gibi yerel bir değişkene sokmaktan başka delege inşa ederken cashOnHand "dereference" yapabileceğim herhangi bir yolu var mı?

Ben Geri Al yapmak için daha iyi yollar hakkında işitme gerçekten ilgilenmiyorum. Lütfen bu konuyu daha net bir şekilde ortaya koyabilmek için sadece bir örnek verin.

+1

_Edit_, Undo demek istediğini varsayalım. – SLaks

+0

Oops, haklısın. Soruyu çözdüm. –

cevap

6

Genel olarak, bunu zaten yaptığınız şeyden başka mükemmel bir çözüm yoktur.

Ancak, belirli durumlarda, böyle bir currier yapabilirsiniz: En yöntemi diğer parametreleri alır

Curry(setCashOnHand, this.cashOnHand); 

, şunları yapabilirsiniz:

static Action Curry(Action<T> method, T param) { return() => method(param); } 

Böyle kullanabilirsiniz Bunu şu şekilde kullanın:

Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand); 
+1

Bu en iyi alternatif gibi görünüyor. Ama çok kafa karıştırıcı, bence değeri yerel bir değişkene kopyalamakla uğraşacağım. –

+0

Temizlik vs Bakım. =) –

2

Yoo, sen o belirli bir zamanda değerlendirilir isterseniz lambda dışında örnek değişkeninin değerini yakalamak gerekecek. gerçekte olduğunda, değerlendirme ilan yöntem vücudun bir parçası olmak göründüğü için

public void SetCashOnHand(int x) { 
    this.cashOnHand = x; 
    undoStack.Push(UndoSetCashOnHand); 
} 

private void UndoSetCashOnHand() 
{ 
    this.setCashOnHand(this.cashOnHand); 
} 

lambda sözdizimi sadece biraz kafa karıştırıcı hale:

araçlarının bu yazılı olsaydı farklı değil otomatik olarak oluşturulmuş bir özel işlevin parçası, diğer işlevler gibi değerlendirilir.

kişi, genel olarak, bu almak bir parametreli fonksiyonunu kullanarak ve yığın bir parçası olarak işlev görmek üzere iletilir değeri hesaplanması ve saklanması gereğidir. Ancak, bir parametreleştirilmiş az fonksiyonu ile gitmek isterseniz, yerel bir bu bilgileri yakalama gerekecek.

+1

Bunun işe yaradığını sanmıyorum .... UndoSetCashOnHand çağrıldığında, o zaman SetCOH zamanında değil, geri ödeme zamanında cashOnHand değerini alır. – Jimmy

+1

@Jimmy: Tam olarak konu bu. Bu onun lambda'sı *, ve bunu yapmanın bir yolu olup olmadığını soruyor. Yaptığım nokta, mantıklı olmaz. Kod alternatif bir çözüm olmayı amaçlamıyordu, aslında ne yaptığını alternatif bir temsil *. –

+0

Anladım. Yanlış anlaşılma için özür dilerim. – Jimmy

0

gerçekten istediğiniz şeyi yapmak başka sihirli bir yolu yoktur. Referans lambda yürütüldüğünde, daha önce değil değerlendirilir. Lambda, yaptığınız şeyden başka bir şey oluşturulduğunda kapatılan değeri "kancalamak" için otomatik bir yol yoktur.

0

Temsilci gelecekte devam ediyor .. I temp değişkeninin muhtemelen "gelecekte kodu çalıştırmak yerine," işlemek için en iyi yol olduğunu düşünün. Ancak, ben Elçi olarak bir akım değerini bağlayan bir Bind() işlevini ..

Action Bind<T>(Func<T> f, T value) { 
    return (Action)(() => f(value)); 
} 
undoStack.Push(Bind(this.setCashOnHand, this.cashOnHand)); 
+2

oops, Curry daha iyi bir isim :) – Jimmy

0

Eh, senin Geri Al yığını kendi eylemleri geri alma edilecektir örneği tarafından sadece başvuruda bulunulan, o zaman yazabilirsiniz tahmin önemli değil. Örneğin

:

public class MyType 
{ 

    private UndoStack undoStack {get;set;} 

    public void SetCashOnHand(int x) { 
    int coh = this.cashOnHand; 
    undo u =() => this.setCashOnHand(coh); 
    this.cashOnHand = x; 
    undoStack.Push(u); 
    } 
} 

sonra ne UndoStack referanslar önemli değil. Bir UndoStack enjekte ederseniz ve bu örneğe referanslar başka yerde tutulursa, bu önemli olur.

İkincisi doğruysa, yığını geri alınma yoluyla örneklerin sızmasını önlemek için, bir tür zayıf referans veya başka bir chicanery bulmaya çalışmak yerine bunu yeniden düzenlemeyi düşünürdüm.

Bu durumda, her bir seviyenin kendi Undo yığınına sahip olması gerekir. Örneğin, UI katmanı, bir daha sonra geri alınması gereken örneği çıkaracak bir geri alma kümesine sahip olmalıdır. Ya da, tek bir kullanıcı tıklaması gerektiğinde, birden fazla farklı türde birden fazla örneğe gereksinim duyabileceği gibi, geri çekilmesi gereken örnek grupları sırayla bir geri alma prosedüründen geçer.

İlgili konular