【问题标题】:portable library with Xamarin - .NET Standard vs. UWP带有 Xamarin 的可移植库 - .NET Standard 与 UWP
【发布时间】:2018-12-31 10:54:47
【问题描述】:

我有以下情况:

  • Assembly X 是我正在构建的 Xamarin 类库。它面向 iOS、Android 和 .NET Standard 2.0。
  • Assembly X 引用 Xamarin.Essentials 中的可移植性 API。
  • Xamarin.Essentials 面向 iOS、Android、.NET Standard 和 UWP (uap10.0.16299)。
  • 现在,有人想使用 Assembly X 构建 Windows 10 应用。
  • 由于他们在 Windows 10 中,我希望他们获得 Xamarin.Essentials 的 UWP 版本。但在我看来,它总是引入 .NET Standard 版本,大概是因为 X 使用的是 .NET Standard 版本。结果是他们无法获得 Xamarin.Essentials 将提供的特定于 Windows 的行为。

有没有办法解决这个问题?还是我也必须构建 X 的 UWP 版本?到目前为止我一直避免这样做的原因是,我将有一些只能在 Windows 上构建的目标,而另一些我只能在 Mac 上构建的目标。

【问题讨论】:

    标签: .net xamarin uwp


    【解决方案1】:

    您不必以某种特定于平台的方式构建您的自定义.NetStd 程序集。您针对 Xamarin.Essentials .NetStd 参考程序集进行编译,然后捆绑/包含 Xamarin.Essentials 程序集的平台特定,其中包含平台相关的代码。

    因此,在运行时,您的自定义 .NetStd 程序集中的类型将被转发/调用到您的应用中已包含的 Xamarin.Essentials 平台特定程序集。

    因此,在包含Xamarin.Essentials 的自定义.NetStd 程序集中,将有一个外部程序集定义:

    .assembly extern Xamarin.Essentials
    {
      .ver 1:0:0:0
    }
    

    然后在同一个.NetStd 程序集中将是调用程序集中存在的类型的代码,即:OpenAppPackageFileAsync

    IL_0030:  call [System.Threading.Tasks]System.Threading.Tasks.Task`1<[System.IO]System.IO.Stream> [Xamarin.Essentials]Xamarin.Essentials.FileSystem::OpenAppPackageFileAsync(string)
    

    注意:在 Essentials 的情况下,它不使用 DI/Interfaces 调用平台相关代码,方法 OpenAppPackageFileAsync 调用 PlatformOpenAppPackageFileAsync 方法,并且该方法将在每个方法中包含特定于平台的代码其平台相关程序集。在其基于 .NetStd 的参考组装方法中,它会抛出一个 NotImplementedInReferenceAssemblyException,因为没有通用/通用的基于 .NetStd 的框架代码来实现加载只读应用程序捆绑的 Android/iOS/UWP 文件,即:

    .method private hidebysig static [System.Threading.Tasks]System.Threading.Tasks.Task`1<[System.IO]System.IO.Stream>
              PlatformOpenAppPackageFileAsync(string filename) cil managed
      {
        // Code size       6 (0x6)
        .maxstack  8
        IL_0000:  newobj     instance void Xamarin.Essentials.NotImplementedInReferenceAssemblyException::.ctor()
        IL_0005:  throw
      } // end of method FileSystem::PlatformOpenAppPackageFileAsync
    

    现在,当您打包/捆绑依赖于平台的应用程序时,例如本例中的 Android,您将不会处理/包含 Essentials 参考程序集,而是特定于平台的版本。

    Xamarin.Essentials.dll 程序集的 Android 版本包括 PlatformOpenAppPackageFileAsync 方法:

    .method private hidebysig static [mscorlib]System.Threading.Tasks.Task`1<[mscorlib]System.IO.Stream>
              PlatformOpenAppPackageFileAsync(string filename) cil managed
      {
        // Code size       70 (0x46)
        .maxstack  3
        .locals init ([mscorlib]System.Threading.Tasks.Task`1<[mscorlib]System.IO.Stream> V_0,
                 [Mono.Android]Java.IO.FileNotFoundException V_1)
        IL_0000:  ldarg.0
        IL_0001:  brtrue.s   IL_000e
        ~~~~
        .try
        {
          IL_001d:  call       [Mono.Android]Android.Content.Context Xamarin.Essentials.Platform::get_AppContext()
          IL_0022:  callvirt   instance [Mono.Android]Android.Content.Res.AssetManager [Mono.Android]Android.Content.Context::get_Assets()
          IL_0027:  ldarg.0
          IL_0028:  callvirt   instance [mscorlib]System.IO.Stream [Mono.Android]Android.Content.Res.AssetManager::Open(string)
          IL_002d:  call       [mscorlib]System.Threading.Tasks.Task`1<!!0> [mscorlib]System.Threading.Tasks.Task::FromResult<[mscorlib]System.IO.Stream>(!!0)
          IL_0032:  stloc.0
          IL_0033:  leave.s    IL_0044
          ~~~
    

    【讨论】:

    • 我无法遵循您示例中的字节码,但我是否正确理解答案基本上是这样的:在主机应用程序中明确包含Xamarin.Essentials,而不仅仅是依赖传递从我的类库中依赖它?
    • @E.Bishop 正确,每个主机应用程序都必须包含/捆绑与参考程序集“匹配”的程序集的“特殊”平台相关版本(包含的.assembly名称/版本/类型/ ...)您编译了包含您的用户代码的 .NetStd 库
    • 不幸的是,据我所知,这在实践中行不通。将 Xamarin.Essentials 包直接添加到我的主机应用程序后,当从类库调用它时,它似乎仍然从 Xamarin.Essentials 获取 .NET Standard 代码 - 仍然得到 NotImplementedInReferenceAssemblyException 就是证明。
    • @E.Bishop 主机应用平台是什么?
    • 哦等等...我以为这是一个 UWP 项目,但实际上它是 .NET 4.6。我会用 UWP 再试一次。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    • 2018-06-23
    相关资源
    最近更新 更多