Anonim sınıf arayüz uygulayabilir mi?


463

Anonim tipte bir arabirim uygulamak mümkün müdür?

Çalışmak istediğim bir kod parçam var, ama bunu nasıl yapacağımı bilmiyorum.

Hayır diyen ya da arayüzün yeni örneklerini inşa eden bir sınıf yaratan birkaç cevap aldım. Bu gerçekten ideal değil, ama bunu basitleştirecek bir arayüzün üstünde ince bir dinamik sınıf oluşturmak için bir mekanizma olup olmadığını merak ediyorum.

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Bir yaklaşım açıklayan bir dinamik arayüz kaydırma makalesi buldum . Bunu yapmanın en iyi yolu bu mu?


1
Bağlantı güncel değil, bu uygun bir alternatif olabilir liensberger.it/web/blog/?p=298 .
Phil Cooper

1
Evet, kullanarak, .NET 4 ve üstü (DLR yoluyla) ile yapabilirsiniz ImpromptuInterface Nuget paketi.
BrainSlugs83

1
@PhilCooper Bağlantınız kesildi, muhtemelen en az 2016'dan beri - ama neyse ki daha önce arşivlendi. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

Yanıtlar:


361

Hayır, anonim türler bir arabirim uygulayamaz. Gönderen C # programlama rehberi :

Anonim türler, bir veya daha fazla genel salt okunur özellikten oluşan sınıf türleridir. Yöntemler veya olaylar gibi başka sınıf üyelerine izin verilmez. Anonim bir tür, nesne dışındaki herhangi bir arabirime veya türe kullanılamaz.


5
zaten bu şeylere sahip olmak güzel olurdu. Kod okunabilirliğinden bahsediyorsanız, lambda ifadeleri genellikle gitmenin yolu değildir. RAD'den bahsediyorsak, java benzeri anonim arayüz uygulamasına giriyorum. Bu arada, bazı durumlarda bu özellik delegelerden daha güçlü
Arsen Zahray

18
@ ArsenZahray: lambda ifadeleri, iyi kullanıldığında kod okunabilirliğini artırır. Yerel değişkenlere olan ihtiyacı azaltabilen veya ortadan kaldırabilen fonksiyonel zincirlerde kullanıldıklarında özellikle güçlüdürler.
Roy Tinker

2
Bu şekilde hile yapabilirsiniz "Anonim Uygulama Sınıfları - C # için Bir Tasarım Deseni" - twistedoakstudios.com/blog/…
Dmitry Pavlov

3
@DmitryPavlov, bu şaşırtıcı derecede değerliydi. Yoldan geçenler: İşte kısaltılmış versiyon.
kdbanman

1
anonim türü aynı alanlarla anonim bir nesneye
atabilirsiniz

90

Bu iki yıllık bir soru olabilir ve iş parçacığındaki cevapların hepsi yeterince doğru olsa da, anonim bir sınıfın bir arabirim uygulamasının aslında bir arabirim uygulamasının mümkün olduğunu söyleme isteğine karşı koyamam biraz yaratıcı hile oraya.

2008'de o zamanlar işverenim için özel bir LINQ sağlayıcısı yazıyordum ve bir noktada anonim sınıflarıma diğer anonim sınıflardan anlatabiliyordum, bu da çek yazmak için kullanabileceğim bir arabirim kullanmaları anlamına geliyordu onlar. Çözdüğümüz yol , arayüz uygulamasını doğrudan IL'ye eklemek için yönleri ( PostSharp kullandık ) kullanmaktı . Aslında, anonim sınıfların arayüzleri uygulamasına izin vermek yapılabilir , sadece oraya ulaşmak için kuralları hafifçe bükmeniz gerekir.


8
@Gusdor, bu durumda, yapı üzerinde tam kontrole sahiptik ve her zaman özel bir makinede çalıştırıldı. Ayrıca, PostSharp kullandığımız ve yaptığımız şey tamamen bu çerçevede yasal olduğundan, PostSharp'ın kullandığımız derleme sunucusuna yüklendiğinden emin olmadıkça hiçbir şey gerçekten pop olamazdı.
Mia Clarke

16
@Gusdor Diğer programcıların projeyi almasının ve çok fazla zorluk çekmeden derlemesinin kolay olması gerektiğine katılıyorum, ancak bu, kalıplama veya postharp gibi çerçevelerden tamamen kaçınmadan ayrı ayrı ele alınabilen farklı bir konudur. Yaptığınız aynı argüman VS'nin kendisine veya C # spesifikasyonunun bir parçası olmayan standart olmayan herhangi bir MS çerçevesine karşı yapılabilir. Bu şeylere ihtiyacınız var, yoksa "pop gidiyor". Bu bir sorun IMO değil. Sorun, yapı o kadar karmaşık hale geldiğinde, bu şeylerin birlikte doğru çalışmasını sağlamak zor.
AaronLS

6
@ZainRizvi Hayır, olmadı. Bildiğim kadarıyla hala üretimde. Ortaya çıkan endişe o zaman benim için garip ve şimdi en iyi ihtimalle benim için keyfi görünüyordu. Temelde "Çerçeve kullanma, işler kırılacak!" Diyordu. Yapmadılar, hiçbir şey patlamadı ve şaşırmadım.
Mia Clarke

3
Artık çok daha kolay; kod oluşturulduktan sonra IL'nin değiştirilmesine gerek yoktur; sadece EnhanmptuInterface kullanın . - Herhangi bir nesneyi (anonim olarak yazılan nesneler dahil) herhangi bir arabirime bağlamanızı sağlar (tabii ki sınıfın gerçekten desteklemediği arabirimin bir bölümünü kullanmaya çalışırsanız geç bağlama istisnaları olacaktır).
BrainSlugs83

44

Arayüzlere anonim türler dökmek bir süredir istediğim bir şeydi, ancak maalesef mevcut uygulama sizi bu arayüzün bir uygulamasını yapmaya zorluyor.

Etrafındaki en iyi çözüm, sizin için uygulamayı oluşturan bir tür dinamik proxy'ye sahip olmaktır. Mükemmel kullanma Linfu projesini sen yerini alabilir

select new
{
  A = value.A,
  B = value.C + "_" + value.D
};

ile

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();

17
Doğaçlama-Arabirim Projesi bunu DLR kullanarak .NET 4.0'da gerçekleştirir ve Linfu'dan daha hafiftir.
jbtule

DynamicObjectbir Linfu tipi? System.Dynamic.DynamicObjectyalnızca korumalı bir kurucuya sahiptir (en azından .NET 4.5'te).
jdmcnair

Evet. DynamicObjectDLR versiyonundan önce gelen LinFu uygulamasına atıfta bulunuyordum
Arne Claassen

15

Anonim türler arabirimleri dinamik bir proxy üzerinden uygulayabilir.

Bu senaryoyu desteklemek için GitHub'da bir uzantı yöntemi ve http://wblo.gs/feE bir blog yazısı yazdım .

Yöntem şu şekilde kullanılabilir:

class Program
{
    static void Main(string[] args)
    {
        var developer = new { Name = "Jason Bowers" };

        PrintDeveloperName(developer.DuckCast<IDeveloper>());

        Console.ReadKey();
    }

    private static void PrintDeveloperName(IDeveloper developer)
    {
        Console.WriteLine(developer.Name);
    }
}

public interface IDeveloper
{
    string Name { get; }
}

13

Hayır; anonim tip, birkaç özelliğe sahip olmak dışında hiçbir şey yapılamaz. Kendi türünüzü oluşturmanız gerekir. Bağlantılı makaleyi derinlemesine okumadım, ancak Reflection kullanıyor gibi görünüyor. Anında yeni türler oluşturmak için kaydolun; ancak tartışmayı C # içindeki şeylerle sınırlarsanız, istediğinizi yapamazsınız.


Ve not etmek önemlidir: özellikler işlevler veya boşluklar da içerebilir (Eylem): select {{MyFunction = new Func <string, bool> (s => value.A == s)} işe yarayamasanız da çalışır ("value.A" yerine "A" kullanamayız).
cfeduke

2
Peki, bu sadece delege olan bir mülk değil mi? Aslında bir yöntem değil.
Marc Gravell

1
Ben çalışma zamanında türleri oluşturmak için Reflection.Emit kullandım ama şimdi çalışma zamanı maliyetlerini önlemek için bir AOP çözümü tercih ediyorum inanıyorum.
Norman H

11

En iyi çözüm, anonim sınıflar kullanmak değildir.

public class Test
{
    class DummyInterfaceImplementor : IDummyInterface
    {
        public string A { get; set; }
        public string B { get; set; }
    }

    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new DummyInterfaceImplementor()
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values.Cast<IDummyInterface>());

    }

    public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Sorgunun sonucunu arabirimin türüne yayınlamanız gerektiğini unutmayın. Bunu yapmanın daha iyi bir yolu olabilir, ama bulamadım.


2
values.OfType<IDummyInterface>()Oyuncular yerine kullanabilirsiniz . Yalnızca koleksiyonunuzdaki bu türe dönüştürülebilen nesneleri döndürür. Tamamen senin ne istediğine bağlı.
Kristoffer L

7

Özellikle sorulan sorunun cevabı hayırdır. Ama alaycı çerçevelere mi bakıyorsunuz? Ben ADEDI kullanın ama milyonlarca orada var ve onlar / satır (kısmen veya tamamen) in-line uygulamak için izin verir. Örneğin.

public void ThisWillWork()
{
    var source = new DummySource[0];
    var mock = new Mock<DummyInterface>();

    mock.SetupProperty(m => m.A, source.Select(s => s.A));
    mock.SetupProperty(m => m.B, source.Select(s => s.C + "_" + s.D));

    DoSomethingWithDummyInterface(mock.Object);
}

1

Başka bir seçenek, kurucuya lambdas alan tek, somut bir uygulama sınıfı oluşturmaktır.

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

// "Generic" implementing class
public class Dummy : DummyInterface
{
    private readonly Func<string> _getA;
    private readonly Func<string> _getB;

    public Dummy(Func<string> getA, Func<string> getB)
    {
        _getA = getA;
        _getB = getB;
    }

    public string A => _getA();

    public string B => _getB();
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new Dummy // Syntax changes slightly
                     (
                         getA: () => value.A,
                         getB: () => value.C + "_" + value.D
                     );

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Hiç yapacağız tüm convert ise DummySourcehiç DummyInterface, o zaman sadece alır bir sınıf var etmek kolay olurdu DummySourceyapıcı ve uygular arayüz içinde.

Ancak, birçok türü dönüştürmeniz gerekiyorsa DummyInterface, bu çok daha az kazan plakasıdır.

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.