C # 'da bir uzantı yöntemiyle bir sınıf yöntemini geçersiz kılmanın herhangi bir yolu var mı?


100

Bir sınıftaki bir yöntemi bir uzantı yöntemiyle geçersiz kılmak istediğim durumlar olmuştur. Bunu C # ile yapmanın bir yolu var mı?

Örneğin:

public static class StringExtension
{
    public static int GetHashCode(this string inStr)
    {
        return MyHash(inStr);
    }
}

Bunu yapmak istediğim bir durum, bir dizenin karmasını bir veritabanına depolayabilmek ve aynı değerin dize sınıfının karmasını kullanan tüm sınıflar tarafından kullanılmasını sağlamaktır (ör. Sözlük, vb.) yerleşik .Net hashing algoritmasının Framework'ün bir sürümünden diğerine uyumlu olduğu garanti edilmiyor, bunu kendi başıma değiştirmek istiyorum.

Bir uzantı yöntemiyle bir sınıf yöntemini geçersiz kılmak istediğim yerde karşılaştığım başka durumlar da var, bu nedenle yalnızca dize sınıfına veya GetHashCode yöntemine özgü değil.

Bunu mevcut bir sınıfın alt sınıflarına ayırarak yapabileceğimi biliyorum, ancak birçok durumda bunu bir uzantı ile yapabilmek kullanışlı olurdu.


Sözlük bir bellek içi veri yapısı olduğundan, karma algoritmanın çerçevenin bir sürümünden diğerine geçip geçmemesi ne fark eder? Çerçeve sürümü değişirse, açıkçası uygulama yeniden başlatılmış ve sözlük yeniden oluşturulmuştur.
David Nelson

Yanıtlar:


94

Hayır; bir uzantı yöntemi hiçbir zaman uygun bir imzaya sahip bir örnek yöntemine göre öncelik kazanmaz ve hiçbir zaman çok biçimliliğe katılmaz ( GetHashCodebir virtualyöntemdir).


"... polimorfizme asla katılmaz (GetHashCode sanal bir yöntemdir)" Bir uzantı yönteminin, sanal olduğu için neden polimorfizme asla katılmadığını başka bir şekilde açıklayabilir misiniz? Bu iki ifade arasındaki bağlantıyı görmekte sorun yaşıyorum.
Alex

3
@Alex, virtual'den bahsederek sadece polimorfik olmanın ne anlama geldiğini açıklıyorum. GetHashCode'un neredeyse tüm kullanımlarında, beton türü bilinmemektedir - bu nedenle çok biçimlilik işin içinde. Bu nedenle, uzantı yöntemleri normal derleyicide öncelik alsalar bile yardımcı olmaz . OP'nin gerçekten istediği şey maymuna yama yapmak. Hangi c # ve .net kolaylaştırmaz.
Marc Gravell

4
Bu üzücü, C # 'da pek çok yanıltıcı anlamsız yöntem, örneğin .ToString()diziler gibi . Kesinlikle gerekli bir özelliktir.
Hi-Angel

O halde neden bir derleyici hatası almıyorsunuz? Örneğin ben yazıyorum. namespace System { public static class guilty { public static string ToLower(this string s) { return s.ToUpper() + " I'm Malicious"; } } }
LowLevel

@LowLevel neden?
Marc Gravell

7

Yöntemin farklı bir imzası varsa, o zaman yapılabilir - yani sizin durumunuzda: hayır.

Ancak aksi halde aradığınız şeyi yapmak için mirası kullanmanız gerekir.


2

Bildiğim kadarıyla cevap hayır, çünkü bir uzantı yöntemi bir örnek değil, daha çok bir sınıf örneğini kullanarak statik bir yöntemi çağırmanıza izin veren bir intellisense tesisi gibi. Bence probleminize bir çözüm, belirli bir yöntemin (ör. GetHashCode ()) yürütülmesini engelleyen ve başka bir şey yapan bir engelleyici olabilir. Böyle bir durdurucu kullanmak için (Castle Projesi'nin sağladığı gibi) tüm nesneler bir nesne fabrikası (veya Castle'daki bir IoC konteyneri), böylece arayüzleri çalışma zamanında oluşturulan dinamik bir proxy aracılığıyla ele geçirilebilir. (Caslte ayrıca sınıfların sanal üyelerini yakalamanıza izin verir)


0

Bir sınıf yöntemiyle aynı imzaya sahip bir uzantı yöntemini çağırmanın bir yolunu buldum, ancak çok zarif görünmüyor. Uzantı yöntemleriyle uğraşırken bazı belgelenmemiş davranışlar fark ettim. Basit kod:

public static class TestableExtensions
{
    public static string GetDesc(this ITestable ele)
    {
        return "Extension GetDesc";
    }

    public static void ValDesc(this ITestable ele, string choice)
    {
        if (choice == "ext def")
        {
            Console.WriteLine($"Base.Ext.Ext.GetDesc: {ele.GetDesc()}");
        }
        else if (choice == "ext base" && ele is BaseTest b)
        {
            Console.WriteLine($"Base.Ext.Base.GetDesc: {b.BaseFunc()}");
        }
    }

    public static string ExtFunc(this ITestable ele)
    {
        return ele.GetDesc();
    }

    public static void ExtAction(this ITestable ele, string choice)
    {
        ele.ValDesc(choice);
    }
}

public interface ITestable
{

}

public class BaseTest : ITestable
{
    public string GetDesc()
    {
        return "Base GetDesc";
    }

    public void ValDesc(string choice)
    {
        if (choice == "")
        {
            Console.WriteLine($"Base.GetDesc: {GetDesc()}");
        }
        else if (choice == "ext")
        {
            Console.WriteLine($"Base.Ext.GetDesc: {this.ExtFunc()}");
        }
        else
        {
            this.ExtAction(choice);
        }
    }

    public string BaseFunc()
    {
        return GetDesc();
    }
}

Fark ettiğim şey, bir uzantı yönteminin içinden ikinci bir yöntemi çağırırsam, imzayla eşleşen bir sınıf yöntemi olsa bile imzayla eşleşen uzantı yöntemini çağıracaktı. Örneğin yukarıdaki kodda, sırayla ele.GetDesc () 'i çağıran ExtFunc ()' ı çağırdığımda, beklediğimiz "Temel GetDesc" dizesi yerine "Uzantı GetDesc" dönüş dizesini alıyorum.

Kodu test etmek:

var bt = new BaseTest();
bt.ValDesc("");
//Output is Base.GetDesc: Base GetDesc
bt.ValDesc("ext");
//Output is Base.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext def");
//Output is Base.Ext.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext base");
//Output is Base.Ext.Base.GetDesc: Base GetDesc

Bu, sınıf yöntemleri ve genişletme yöntemleri arasında istediğiniz zaman gidip gelmenize izin verir, ancak sizi istediğiniz "kapsam" a sokmak için yinelenen "geçiş" yöntemlerinin eklenmesini gerektirir. Daha iyi bir kelime olmadığı için burada buna kapsam diyorum. Umarım birisi bana gerçekte ne dendiğini bildirebilir.

Tek bir yöntemin veya iki yöntemin aynı imzaya sahip birden çok yöntem için geçiş işlevi görebileceği umuduyla, delegeleri onlara iletme fikriyle de oynadığım "geçiş" yöntem isimlerimden tahmin etmiş olabilirsiniz. Maalesef, temsilci paketinden çıkarıldıktan sonra, başka bir uzantı yönteminin içinden bile olsa, her zaman uzantı yöntemi yerine sınıf yöntemini seçti. "Kapsam" artık önemli değil. Action and Func delegelerini pek kullanmadım, bu yüzden belki daha deneyimli biri bu kısmı çözebilir.

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.