针对目标的 .NET 嵌入最佳做法-C.NET Embedding best practices for Objective-C

这是一个草稿,可能不与该工具当前支持的功能同步。This is a draft and might not be in-sync with the features presently supported by the tool. 我们希望本文档单独发展,并最终与最终工具匹配,也就是说,我们将建议长期的最佳方法,而不是即时解决方法。We hope that this document will evolve separately and eventually match the final tool, i.e. we'll suggest long term best approaches - not immediate workarounds.

本文档中的大部分内容也适用于其他受支持的语言。A large part of this document also applies to other supported languages. 但提供的所有示例都C#在和目标-C 中。However all provided examples are in C# and Objective-C.

公开托管代码的子集Exposing a subset of the managed code

生成的本机库/框架包含目标-C 代码,以调用公开的每个托管 Api。The generated native library/framework contains Objective-C code to call each of the managed APIs that is exposed. 您表面上的更多 API (使其成为公共的)越大,本机_粘连_库就会变成。The more API you surface (make public) then larger the native glue library will become.

最好创建一个不同的小型程序集,以便仅向本机开发人员公开所需的 Api。It might be a good idea to create a different, smaller assembly, to expose only the required APIs to the native developer. 此外观还允许您更好地控制可见性、命名、错误检查 ...生成的代码的。That facade will also allow you more control over the visibility, naming, error checking... of the generated code.

公开 chunkier APIExposing a chunkier API

从本机过渡到托管(和后退)都需要支付价格。There is a price to pay to transition from native to managed (and back). 因此,更好的做法是将_块式(而不是_向本机开发人员)公开的 api,例如As such, it's better to expose chunky instead of chatty APIs to the native developers, e.g.

聊天Chatty

public class Person {
  public string FirstName { get; set; }
  public string LastName { get; set; }
}
// this requires 3 calls / transitions to initialize the instance
Person *p = [[Person alloc] init];
p.firstName = @"Sebastien";
p.lastName = @"Pouliot";

块式Chunky

public class Person {
  public Person (string firstName, string lastName) {}
}
// a single call / transition will perform better
Person *p = [[Person alloc] initWithFirstName:@"Sebastien" lastName:@"Pouliot"];

由于转换的数量较小,因此性能将更好。Since the number of transitions is smaller the performance will be better. 它还需要生成较少的代码,因此,这也会生成较小的本机库。It also requires less code to be generated, so this will produce a smaller native library as well.

命名Naming

命名功能是计算机科学中的两个最难解决的问题之一,另一个则是缓存失效,而不是由1个错误引发。Naming things is one of two hardest problems in computer science, the others being cache invalidation and off-by-1 errors. 但愿 .NET 嵌入会使你免受除命名以外的一切的防御。Hopefully .NET Embedding can shield you from all but naming.

类型Types

目标-C 不支持命名空间。Objective-C does not support namespaces. 通常,其类型带有2(表示 Apple)或3(对于第三方)字符前缀(如 UIKit 的视图 UIView,它表示框架)。In general, its types are prefixed with a 2 (for Apple) or 3 (for 3rd parties) character prefix, like UIView for UIKit's View, which denotes the framework.

对于 .NET 类型,不能跳过命名空间,因为它会引入重复或混乱的名称。For .NET types skipping the namespace is not possible as it can introduce duplicated, or confusing, names. 这使得现有的 .NET 类型非常长,例如This makes existing .NET types very long, e.g.

namespace Xamarin.Xml.Configuration {
  public class Reader {}
}

使用方式类似于:would be used like:

id reader = [[Xamarin_Xml_Configuration_Reader alloc] init];

但是,可以将类型重新公开为:However you can re-expose the type as:

public class XAMXmlConfigReader : Xamarin.Xml.Configuration.Reader {}

使其更易于使用,例如:making it more Objective-C friendly to use, e.g.:

id reader = [[XAMXmlConfigReader alloc] init];

方法Methods

即使是良好的 .NET 名称也可能不适合目标-C API。Even good .NET names might not be ideal for an Objective-C API.

目标-C 中的命名约定与 .NET 不同(camel 大小写,而不是 pascal 大小写,更详细)。Naming conventions in Objective-C are different than .NET (camel case instead of pascal case, more verbose). 请参阅Cocoa 的编码指导原则Please read the coding guidelines for Cocoa.

从目标-C 开发人员的角度来看,具有 Get 前缀的方法意味着您不拥有该实例,即get 规则From an Objective-C developer's point of view, a method with a Get prefix implies you do not own the instance, i.e. the get rule.

此命名规则在 .NET GC 世界中没有匹配项;使用 Create 前缀的 .NET 方法在 .NET 中的行为相同。This naming rule has no match in the .NET GC world; a .NET method with a Create prefix will behave identically in .NET. 但对于目标为 C 的开发人员,这通常意味着拥有返回的实例,即创建规则However, for Objective-C developers, it normally means you own the returned instance, i.e. the create rule.

异常Exceptions

.NET 中很常见的情况是经常使用异常来报告错误。It's quite common in .NET to use exceptions extensively to report errors. 但是,它们速度慢,但在目标-C 中并不完全相同。However, they are slow and not quite identical in Objective-C. 应尽可能将它们从目标-C 开发人员隐藏。Whenever possible you should hide them from the Objective-C developer.

例如,.NET Try 模式从目标-C 代码更容易使用:For example, the .NET Try pattern will be much easier to consume from Objective-C code:

public int Parse (string number)
{
  return Int32.Parse (number);
}

相对versus

public bool TryParse (string number, out int value)
{
  return Int32.TryParse (number, out value);
}

init* 中的异常Exceptions inside init*

在 .NET 中,构造函数必须成功,并返回(但愿)有效的实例或引发异常。In .NET a constructor must either succeed and return a (hopefully) valid instance or throw an exception.

与此相反,目标-C 允许 init* 在无法创建实例时返回 nilIn contrast, Objective-C allows init* to return nil when an instance cannot be created. 这是许多 Apple 框架中使用的常见(而非一般)模式。This is a common, but not general, pattern used in many of Apple's frameworks. 在某些其他情况下,可能会发生 assert (和终止当前进程)。In some other cases an assert can happen (and kill the current process).

生成器对于生成的 init* 方法遵循相同的 return nil 模式。The generator follow the same return nil pattern for generated init* methods. 如果引发了托管异常,则会将其打印(使用 NSLog),并 nil 将返回到调用方。If a managed exception is thrown, then it will be printed (using NSLog) and nil will be returned to the caller.

运算符Operators

客观-C 不允许运算符重载C# ,因此它们转换为类选择器。Objective-C does not allow operators to be overloaded as C# does, so these are converted to class selectors.

在找到运算符重载时,将按照优先顺序生成"友好"命名方法,从而可以更轻松地使用 API。"Friendly" named methods are generated in preference to the operator overloads when found, and can produce an easier to consume API.

重写运算符 == 和/或 != 的类应同时重写标准 Equals (Object)方法。Classes that override the operators == and\or != should override the standard Equals (Object) method as well.