共用方式為


Xamarin.iOS 中 NSObject 的泛型子類別

搭配 NSObjects 使用泛型

在 的 NSObject子類別中使用泛型,例如 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);
    }
}

因為子類別 NSObject 向 Objective-C 運行時間註冊的物件有一些限制,例如類型之 NSObject 泛型子類別的可能。

NSObject 的泛型子類別考慮

本文件詳細說明 對泛型子類別 NSObjects有限支援的限制。

成員簽章中的泛型類型自變數

公開給 Objective-C 成員簽章中的所有泛型型別自變數都必須具有 NSObject 條件約束。

良好

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

原因:泛型型別參數是 NSObject,因此 可以安全地公開 Objective-C 的myMethod:選取器簽章(一律為 NSObject 或它的子類別)。

不正確

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

原因:無法為導出的成員Objective-C建立Objective-C程式碼可以呼叫的簽章,因為簽章會因泛型型別的確切類型T而有所不同。

良好

class Generic<T> : NSObject
{
    T storage;

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

原因:只要未參與導出的成員簽章,就可以有不受限制的泛型型別自變數。

良好

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

原因T匯出MyMethod中的 Objective-C 參數限制為 NSObject,不受限制的類型U不是簽章的一部分。

不正確

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

}

原因: registrar 尚不支援此案例。 如需詳細資訊,請參閱 此 GitHub 問題

泛型型別的具現化 Objective-C

不允許從 Objective-C 中具現化泛型型別。 這通常會在 xib 或分鏡腳本中使用 Managed 類型時發生。

請考慮此類別定義,其會公開採用 IntPtr 的建構函式(從原生 Objective-C 實例建構 C# 物件的 Xamarin.iOS 方法):

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

雖然上述建構正常,在運行時間,如果 Objective-C 嘗試建立它的實例,這會擲回例外狀況。

這是因為 Objective-C 沒有泛型型別的概念,因此無法指定要建立的確切泛型類型。

建立泛型類型的特製化子類別,即可解決此問題。 例如:

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

class GenericUIView : Generic<UIView>
{
}

現在不再模棱兩可, GenericUIView 類別可以在 xibs 或分鏡腳本中使用。

不支援泛型方法

不允許泛型方法。

下列程式代碼將不會編譯:

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

原因:這是不允許的,因為 Xamarin.iOS 不知道從 叫用 方法Objective-C時,要用於型別自變數T的類型。

替代方法是建立特製化方法,並改為導出:

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

不允許導出的靜態成員

如果靜態成員載入於的NSObject泛型子類別內,則您無法將靜態成員Objective-C公開至 。

不支援案例的範例:

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

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

原因: 就像泛型方法一樣,Xamarin.iOS 運行時間必須知道泛型型別自變數 T要使用的類型。

針對實例成員,實例本身會使用 (因為永遠不會有 實例 Generic<T>,所以一律會 Generic<SomeSpecificClass>是 ),但對於靜態成員而言,這項資訊不存在。

請注意,即使有問題的成員以任何方式不使用類型自變數 T ,也是如此。

在此情況下,替代方式是建立特製化子類別:

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

效能

靜態 registrar 無法在建置階段解析泛型型別中的導出成員,因為它通常一樣,必須在運行時間查閱。 這表示從叫用這類方法 Objective-C 會比從非泛型類別叫用成員稍微慢一點。