2013-04-22 37 views
6

Bu yüzden asp.net mvc'de ui denetimleri üretmek için bir nesne hiyerarşisi alıyorum ve akıcı bir api elde etmeye çalışıyorum. Mevcut probleme odaklanmak için kukla bir sınıf yapıyorum..NET'te uzantı yöntemleri nasıl "geçersiz kılınır"?

LinkButton lb = new LinkButton().Id("asd"); 

Gerçekten derleyici seçmek düşündüm: Ben bir LinkButton üzerinde kimliği aramaya çalıştığımda

public abstract class HtmlElement { /* ... */ } 

public abstract class UIElement : HtmlElement { /* ... */ } 

public abstract class ButtonBase : UIElement { /* ... */ } 

public class LinkButton : ButtonBase { /* ... */ } 

public class ActionButton : ButtonBase { /* ... */ } 


public static class HtmlElementExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : HtmlElement 
    { 
    /* set the id */ 
    return item; 
    } 
} 

public static class ButtonBaseExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : ButtonBase 
    { 
    /* set the id and do some button specific stuff*/ 
    return item; 
    } 
} 

derleyici belirsiz çağrı var diyor ki: Yani burada
"yanlış" kod temeli Bu durumda en yakın eşleşme, HtmlElement öğesinden devraldığı HtmlExtensions Id yönteminden devralınan bir Komut Dosyası sınıfına sahip olsaydım ve bir Bağlama Düğmesi için (kısıtlamalardan dolayı) ButtonBase yöntemi çağrılır. Bir çözümüm var, ama daha iyi bir tane olduğundan emin değilim.
I ButtonBaseExtensions id silme yöntemi ve aşağıdaki şekilde olduğu gibi HtmlElementExtensions Id yöntemi değiştirilebilir:

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
    /* do some button specific stuff*/ 
    } 
    /* set the id */ 
    return item; 
} 

Bu şekilde ButtonBase her sınıf soyundan çalışmaktadır. Çözümü gerçekten beğenmedim, çünkü HtmlElement mantığını ButtonBase mantığıyla karıştırıyor. Daha iyi bir çözüm için herhangi bir fikir/öneri? Onları farklı ad alanına koydum sanırım, ama sadece bir saniye. Her iki isim alanını da kullanmalıyım, bu yüzden sorunu çözmeyin.

Derleyicinin genel uzantı yöntemleri üzerindeki kısıtlamaları izlemesinin gerektiği fikri olarak msdn forumlarından bahsetmeye değer olduğunu mu düşünüyorsunuz? Bu arada

Biraz daha araştırma yapmak ve msdn forumlarında bir iplik başlamak:
link Bazı olmayan jenerik uzatma methodds çalıştı:

BaseClass bc = new BaseClass(); 
InheritedClass ic = new InheritedClass(); 
BaseClass ic_as_bc = new InheritedClass(); 

bc.SomeMethod("bc"); 
ic.SomeMethod("ic"); 
ic_as_bc.SomeMethod("ic_as_bc"); 
:

public class BaseClass { /*...*/ } 
    public class InheritedClass : BaseClass { /*...*/ } 

    public static class BaseClassExtensions 
    { 
    public static void SomeMethod(this BaseClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("BaseClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

    public static class InheritedClassExtensions 
    { 
    public static void SomeMethod(this InheritedClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("InheritedClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

Ve ben bu örneğini ise

Bu çıktı üretildi:

BaseClassExtensions.SomeMethod called wtih parameter: bc 
InheritedClassExtensions.SomeMethod called wtih parameter: ic 
BaseClassExtensions.SomeMethod called wtih parameter: ic_as_bc 

You can vote it for now

sayesinde
Péter

+1

bir uzantısı yöntemi ** ** Bir sanal yöntemi için yedek oyuncu, ne de sanal yapılabilir değildir. Bu sınıfları kendiniz ilan ettiğin için, neden ihtiyacın olduğu belli değil. Sadece temel sınıflardan birine sanal bir yöntem ekleyin ve gerektiğinde türetilmiş bir sınıfta geçersiz kılın. –

+0

Akıcı api nedeniyle uzatma yöntemleri kullanmam gerekiyor. Jenerik kalıtım sadece 2 seviyeli kalıtsallıkla çalışır. Derleyicinin çağırmak için doğru yöntemi belirlemek için parametre kısıtlamasını kullandığı, genetik olmayan uzantı yöntemiyle bir örnek yazdım. Derleyicinin genel parametre kısıtlamalarını da değerlendirmesini dilerim. Genel Statik T Id (bu T öğesi, dize kimliği) yerine "public Statik ButtonBase Kimliği" (bu ButtonBase öğesi, string id) yazamıyorum. Burada T: ButtonBase "dönüş tipi ButtonBase olacağından akıcı api'nin yöntem zinciri. –

cevap

2

Uzatma yöntemleri hakkında MSDN belgelerine bir göz atabilirsiniz: Extension Methods (C# Programming Guide). ilginç olan Derleme Zamanda de Cilt Uzatma Yöntemleri altındadır:

... ilk türünün örneği yöntemleri bir eşleşme arar. Eşleşme bulunamazsa, tür için tanımlanmış uzantı yöntemlerini arayacak ve bulduğu ilk uzantı yöntemine bağlanacaktır.

Bu yüzden bu davranışı görüyorsunuz. Ve aslında bunu satın alabilirim, sadece birisinin uygulamanızın nasıl çalıştığını sadece bir yöntemle yazabileceğini hayal edin. public static T Id<T>(this T item, string id) where T : object. Ve herhangi bir derleyici hatası görmezseniz, her şeyin doğru olduğunu düşünürsünüz ve belki de nadiren bazı durumlar dışında her şey işe yarayacaktır. Ne kadar kafa karıştırıcı olabilir?

Ve yaklaşımınız hakkında bir daha kötü şey. API'nizi tüketir ve Düğme için bunu görürsem iki yöntemim vardır: biri HtmlElementExtensions ve ButtonBaseExtensions'dan birinde ButtonExtensions.Id(button, "id") yerine HtmlElementExtensions.Id(button, "id")'u ne yapmamı engeller? Senin durumunda

ben kombine yaklaşım tercih ediyorum:

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
     return (T)Id((ButtonBase)item); 
    } 
    else if (item is HtmlElement) 
    { 
     return (T)Id((HtmlElement)item); 
    } 

    throw new NotSupportedException("Type " + item.GetType() + " is not supported by Id extension method"); 
} 

private static ButtonBase Id(ButtonBase item, string id) 
{ 
    return item; 
} 

private static HtmlElement Id(HtmlElement item, string id) 
{ 
    return item; 
} 
+0

Örneğimle, kullanıcı yöntemlerimin üzerine yazamaz, çünkü devralma ağacında ButtonBase, LinkButton'a nesneden çok daha yakındır. Ve gerçekten kötü niyetli bir kullanıcı olduğunu varsayalım. Yeni bir throw atmak için daha basit olduğunu düşünüyorum Exception(); hata yapmak için kod. : D Cevabınız aynı şekilde benim çözümümün, sadece biraz daha şık görünüyorsun :) –

+0

@ Péter yep, sorry. Farklı yönleri düşünüyordum. Eğer bir 'ButtonBase' varsa ve birisi 'LinkButton' için uzantı yöntemi yazacaksa. Ve 'kombine yaklaşım' ile evet, hem çözümlerinizi bir kerede kastediyorum;) – outcoldman

İlgili konular