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 會比從非泛型類別叫用成員稍微慢一點。