Bagikan melalui


Subkelas generik NSObject di Xamarin.iOS

Menggunakan generik dengan NSObjects

Dimungkinkan untuk menggunakan generik dalam subkelas , NSObjectmisalnya UIView:

class Foo<T> : UIView {
    public Foo (CGRect x) : base (x) {}
    public override void Draw (CoreGraphics.CGRect rect)
    {
        Console.WriteLine ("T: {0}. Type: {1}", typeof (T), GetType ().Name);
    }
}

Karena objek yang subkelas NSObject terdaftar dengan Objective-C runtime ada beberapa batasan tentang apa yang mungkin dengan subkelas NSObject jenis generik.

Pertimbangan untuk subkelas generik NSObject

Dokumen ini merinci batasan dalam dukungan terbatas untuk subkelas generik .NSObjects

Argumen Tipe Generik dalam Tanda Tangan Anggota

Semua argumen jenis generik dalam tanda tangan anggota yang diekspos Objective-C harus memiliki NSObject batasan.

Bagus:

class Generic<T> : NSObject where T: NSObject
{
    [Export ("myMethod:")]
    public void MyMethod (T value)
    {
    }
}

Alasan: Parameter jenis generik adalah NSObject, sehingga tanda tangan pemilih untuk myMethod: dapat diekspos Objective-C dengan aman (akan selalu NSObject atau subkelasnya).

Buruk:

class Generic<T> : NSObject
{
    [Export ("myMethod:")]
    public void MyMethod (T value)
    {
    }
}

Alasan: tidak dimungkinkan untuk membuat Objective-C tanda tangan untuk anggota yang diekspor yang Objective-C dapat dipanggil kode, karena tanda tangan akan berbeda tergantung pada jenis yang tepat dari jenis Tgenerik .

Bagus:

class Generic<T> : NSObject
{
    T storage;

    [Export ("myMethod:")]
    public void MyMethod (NSObject value)
    {
    }
}

Alasan: dimungkinkan untuk memiliki argumen jenis generik yang tidak dibatasi selama mereka tidak mengambil bagian dari tanda tangan anggota yang diekspor.

Bagus:

class Generic<T, U> : NSObject where T: NSObject
{
    [Export ("myMethod:")]
    public void MyMethod (T value)
    {
        Console.WriteLine (typeof (U));
    }
}

Alasan: T parameter dalam yang Objective-C diekspor MyMethod dibatasi menjadi NSObject, jenis U yang tidak dibatasi bukan bagian dari tanda tangan.

Buruk:

class Generic<T> : NSObject
{
    public T Storage { get; }

    public Generic(T value)
    {
        Storage = value;
    }
}

[Register("Foo")]
class Foo: NSView
{
    [Export("Test")]
    public Generic<int> Test { get; set; } = new Generic<int>(22);

    [Export("Test1")]
    public Generic<object> Test1 { get; set; } = new Generic<object>(new object());

    [Export("Test2")]
    public Generic<NSObject> Test2 { get; set; } = new Generic<NSObject>(new NSObject());

}

Alasan: registrar belum mendukung skenario ini. Untuk informasi selengkapnya, lihat masalah GitHub ini.

Instansiasi Jenis Generik dari Objective-C

Instansiasi jenis generik dari Objective-C tidak diperbolehkan. Ini biasanya terjadi ketika jenis terkelola digunakan dalam xib atau papan cerita.

Pertimbangkan definisi kelas ini, yang mengekspos konstruktor yang mengambil IntPtr (cara Xamarin.iOS membangun objek C# dari instans asli Objective-C ):

class Generic<T> : NSObject where T : NSObject
{
    public Generic () {}
    public Generic (IntPtr ptr) : base (ptr) {}
}

Meskipun konstruksi di atas baik-baik saja, pada runtime, ini akan melemparkan pengecualian jika Objective-C mencoba membuat instansnya.

Ini terjadi karena Objective-C tidak memiliki konsep jenis generik, dan tidak dapat menentukan jenis generik yang tepat untuk dibuat.

Masalah ini dapat ditangani dengan membuat subkelas khusus dari jenis generik. Contohnya:

class Generic<T> : NSObject where T : NSObject
{
    public Generic () {}
    public Generic (IntPtr ptr) : base (ptr) {}
}

class GenericUIView : Generic<UIView>
{
}

Sekarang tidak ada ambiguitas lagi, kelas GenericUIView dapat digunakan dalam xib atau papan cerita.

Tidak ada dukungan untuk metode generik

Metode generik tidak diperbolehkan.

Kode berikut tidak akan dikompilasi:

class MyClass : NSObject
{
    [Export ("myMethod")]
    public void MyMethod<T> (T argument)
    {
    }
}

Alasan: Ini tidak diizinkan karena Xamarin.iOS tidak tahu jenis mana yang akan digunakan untuk argumen T jenis ketika metode dipanggil dari Objective-C .

Alternatifnya adalah membuat metode khusus dan mengekspornya sebagai gantinya:

class MyClass : NSObject
{
    [Export ("myMethod")]
    public void MyUIViewMethod (UIView argument)
    {
        MyMethod<UIView> (argument);
    }
    public void MyMethod<T> (T argument)
    {
    }
}

Tidak ada anggota statis yang diekspor yang diizinkan

Anda tidak dapat mengekspos anggota Objective-C statis jika dihosting di dalam subkelas generik dari NSObject.

Contoh skenario yang tidak didukung:

class Generic<T> : NSObject where T : NSObject
{
    [Export ("myMethod:")]
    public static void MyMethod ()
    {
    }

    [Export ("myProperty")]
    public static T MyProperty { get; set; }
}

Alasan: Sama seperti metode generik, runtime Xamarin.iOS harus dapat mengetahui jenis apa yang akan digunakan untuk argumen Tjenis generik .

Misalnya anggota instans itu sendiri digunakan (karena tidak akan pernah ada instans Generic<T>, itu akan selalu Generic<SomeSpecificClass>), tetapi untuk anggota statis informasi ini tidak ada.

Perhatikan bahwa ini berlaku bahkan jika anggota yang bersangkutan tidak menggunakan argumen T jenis dengan cara apa pun.

Alternatif dalam hal ini adalah membuat subkelas khusus:

class GenericUIView : Generic<UIView>
{
    [Export ("myUIViewMethod")]
    public static void MyUIViewMethod ()
    {
        MyMethod ();
    }

    [Export ("myProperty")]
    public static UIView MyUIViewProperty {
        get { return MyProperty; }
        set { MyProperty = value; }
    }
}

class Generic<T> : NSObject where T : NSObject
{
    public static void MyMethod () {}
    public static T MyProperty { get; set; }
}

Performa

Statis registrar tidak dapat menyelesaikan anggota yang diekspor dalam jenis generik pada waktu build seperti biasanya, itu harus dicari pada runtime. Ini berarti bahwa memanggil metode seperti itu dari Objective-C sedikit lebih lambat daripada memanggil anggota dari kelas non-generik.