Bisakah kelas anonim mengimplementasikan antarmuka?

463

Apakah mungkin untuk membuat tipe anonim mengimplementasikan antarmuka?

Saya punya kode yang ingin saya kerjakan, tetapi tidak tahu bagaimana melakukan ini.

Saya punya beberapa jawaban yang mengatakan tidak, atau membuat kelas yang mengimplementasikan antarmuka membangun contoh baru dari itu. Ini tidak benar-benar ideal, tetapi saya bertanya-tanya apakah ada mekanisme untuk membuat kelas dinamis tipis di atas antarmuka yang akan membuat ini sederhana.

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);
        }
    }
}

Saya telah menemukan artikel Pembungkus antarmuka dinamis yang menjelaskan satu pendekatan. Apakah ini cara terbaik untuk melakukan ini?

Nick Randell
sumber
1
Tautan muncul kedaluwarsa, ini mungkin alternatif yang cocok liensberger.it/web/blog/?p=298 .
Phil Cooper
1
Ya, Anda bisa melakukan ini dengan .NET 4 dan yang lebih tinggi (melalui DLR), menggunakan paket nuget ImpromptuInterface .
BrainSlugs83
1
@ PhilCooper Tautan Anda telah turun, mungkin sejak setidaknya 2016 - tapi untungnya itu diarsipkan sebelum itu. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

Jawaban:

361

Tidak, tipe anonim tidak dapat mengimplementasikan antarmuka. Dari panduan pemrograman C # :

Tipe anonim adalah tipe kelas yang terdiri dari satu atau lebih properti hanya baca publik. Tidak ada anggota kelas jenis lain seperti metode atau acara yang diizinkan. Jenis anonim tidak dapat dilemparkan ke antarmuka atau jenis apa pun kecuali objek.

HasaniH
sumber
5
akan menyenangkan untuk memiliki barang-barang ini. Jika Anda berbicara keterbacaan kode, ekspresi lambda biasanya bukan cara yang tepat. Jika kita berbicara RAD, saya semua menggunakan implementasi antarmuka anonim seperti java. Omong-omong, dalam beberapa kasus fitur itu lebih kuat daripada delegasi
Arsen Zahray
18
@ArsenZahray: ekspresi lambda, bila digunakan dengan baik, sebenarnya meningkatkan keterbacaan kode. Mereka sangat kuat ketika digunakan dalam rantai fungsional, yang dapat mengurangi atau menghilangkan kebutuhan untuk variabel lokal.
Roy Tinker
2
Anda dapat melakukan trik ini dengan cara "Kelas Implementasi Anonim - Pola Desain untuk C #" - twistedoakstudios.com/blog/…
Dmitry Pavlov
3
@ DmitryPavlov, itu sangat berharga. Passersby: di sini adalah versi kental.
kdbanman
1
Anda dapat melemparkan tipe anonim ke objek anonim dengan bidang yang sama stackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov
90

Meskipun ini mungkin pertanyaan berusia dua tahun, dan sementara jawaban di utas itu semua benar, saya tidak bisa menahan diri untuk memberi tahu Anda bahwa sebenarnya mungkin ada kelas anonim yang mengimplementasikan antarmuka, meskipun itu membutuhkan sedikit kecurangan kreatif untuk sampai ke sana.

Kembali pada tahun 2008 saya menulis penyedia LINQ khusus untuk majikan saya saat itu, dan pada satu titik saya harus bisa memberi tahu kelas anonim "saya" dari yang anonim lainnya, yang berarti meminta mereka mengimplementasikan antarmuka yang dapat saya gunakan untuk mengetikkan centang mereka. Cara kami menyelesaikannya adalah dengan menggunakan aspek (kami menggunakan PostSharp ), untuk menambahkan implementasi antarmuka langsung di IL. Jadi, pada kenyataannya, membiarkan kelas anonim mengimplementasikan antarmuka bisa dilakukan , Anda hanya perlu sedikit membengkokkan aturan untuk sampai ke sana.

Mia Clarke
sumber
8
@ Guldor, dalam hal ini, kami memiliki kontrol penuh atas build, dan selalu dijalankan pada mesin khusus. Juga, karena kami menggunakan PostSharp, dan apa yang kami lakukan adalah sepenuhnya legal dalam kerangka itu, tidak ada yang bisa benar-benar muncul selama kami memastikan PostSharp diinstal pada server build yang kami gunakan.
Mia Clarke
16
@ Goddor Saya setuju bahwa semestinya mudah bagi programmer lain untuk mendapatkan proyek dan kompilasi tanpa banyak kesulitan, tetapi itu adalah masalah yang berbeda yang dapat diatasi secara terpisah, tanpa sepenuhnya menghindari tooling atau kerangka kerja seperti postsharp. Argumen yang sama yang Anda buat dapat dibuat terhadap VS itu sendiri atau kerangka kerja MS non-standar lainnya yang bukan bagian dari spesifikasi C #. Anda membutuhkan hal-hal itu atau "pop pop". Itu bukan masalah IMO. Masalahnya adalah ketika bangunan menjadi sangat rumit sehingga sulit untuk membuat semuanya bekerja bersama dengan benar.
AaronLS
6
@ ZainRizvi Tidak, tidak. Sejauh yang saya tahu, masih dalam produksi. Kekhawatiran yang muncul tampak aneh bagi saya pada waktu itu, dan sewenang-wenang bagi saya sekarang. Itu pada dasarnya mengatakan "Jangan gunakan kerangka kerja, semuanya akan rusak!". Mereka tidak, tidak ada yang muncul, dan saya tidak terkejut.
Mia Clarke
3
Jauh lebih mudah sekarang; tidak perlu memodifikasi IL setelah kode dihasilkan; cukup gunakan ImpromptuInterface . - Ini memungkinkan Anda mengikat objek apa pun (termasuk objek yang diketik secara anonim) ke antarmuka apa pun (tentu saja akan ada pengecualian yang mengikat jika Anda mencoba menggunakan bagian antarmuka yang tidak didukung oleh kelas).
BrainSlugs83
44

Menggunakan tipe anonim ke antarmuka telah menjadi sesuatu yang saya inginkan untuk sementara waktu, tetapi sayangnya implementasi saat ini memaksa Anda untuk memiliki implementasi antarmuka itu.

Solusi terbaik di sekitarnya adalah memiliki beberapa jenis proxy dinamis yang menciptakan implementasi untuk Anda. Menggunakan proyek LinFu yang luar biasa yang dapat Anda ganti

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

dengan

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();
Arne Claassen
sumber
17
Proyek Antarmuka Dadakan akan melakukan ini dalam. NET 4.0 menggunakan DLR dan lebih ringan daripada Linfu.
jbtule
Apakah DynamicObjecttipe LinFu? System.Dynamic.DynamicObjecthanya memiliki konstruktor yang dilindungi (setidaknya dalam .NET 4.5).
jdmcnair
Iya. Saya merujuk pada implementasi LinFu DynamicObjectyang mendahului versi DLR
Arne Claassen
15

Jenis anonim dapat mengimplementasikan antarmuka melalui proxy dinamis.

Saya menulis metode ekstensi di GitHub dan posting blog http://wblo.gs/feE untuk mendukung skenario ini.

Metode ini dapat digunakan seperti ini:

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; }
}
Jason Bowers
sumber
13

Tidak; tipe anonim tidak dapat dibuat untuk melakukan apa pun kecuali memiliki beberapa properti. Anda harus membuat tipe Anda sendiri. Saya tidak membaca artikel tertaut secara mendalam, tetapi sepertinya menggunakan Reflection.Emit untuk membuat jenis baru dengan cepat; tetapi jika Anda membatasi diskusi untuk hal-hal dalam C # itu sendiri, Anda tidak dapat melakukan apa yang Anda inginkan.

Marc Gravell
sumber
Dan penting untuk dicatat: properti dapat mencakup fungsi atau rongga (Aksi) juga: pilih baru {... MyFunction = Fungsi baru <string, bool> (s => value.A == s)} berfungsi meskipun Anda tidak dapat merujuk ke properti baru di fungsi Anda (kami tidak dapat menggunakan "A" sebagai pengganti "value.A").
cfeduke
2
Nah, bukankah itu hanya properti yang kebetulan merupakan delegasi? Ini sebenarnya bukan metode.
Marc Gravell
1
Saya telah menggunakan Reflection.Emit untuk membuat tipe saat runtime tetapi percaya sekarang saya lebih suka solusi AOP untuk menghindari biaya runtime.
Norman H
11

Solusi terbaik adalah tidak menggunakan kelas anonim.

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);
        }
    }
}

Perhatikan bahwa Anda harus memberikan hasil kueri ke jenis antarmuka. Mungkin ada cara yang lebih baik untuk melakukannya, tetapi saya tidak dapat menemukannya.

ICR
sumber
2
Anda bisa menggunakan values.OfType<IDummyInterface>()gantinya. Itu hanya mengembalikan objek dalam koleksi Anda yang sebenarnya bisa dilemparkan ke tipe itu. Itu semua tergantung pada apa yang Anda inginkan.
Kristoffer L
7

Jawaban atas pertanyaan yang diajukan secara spesifik adalah tidak. Tapi apakah Anda sudah melihat kerangka kerja mengejek? Saya menggunakan MOQ tetapi ada jutaan dari mereka di luar sana dan mereka memungkinkan Anda untuk mengimplementasikan / rintisan (sebagian atau sepenuhnya) antarmuka secara online. Misalnya.

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);
}
Sembilan Ekor
sumber
1

Pilihan lain adalah membuat kelas pelaksana tunggal dan konkret yang mengambil lambdas di konstruktor.

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);
        }
    }
}

Jika semua yang akan Anda lakukan adalah mengonversi DummySourceke DummyInterface, maka akan lebih mudah untuk hanya memiliki satu kelas yang mengambil DummySourcekonstruktor dan mengimplementasikan antarmuka.

Tapi, jika Anda perlu mengubah banyak jenis DummyInterface, ini jauh lebih sedikit pelat ketel.

Gordon Bean
sumber