这正是 MvvmCross、ReactiveUI 和其他框架所做的。因此,声称世界上没有人这样做是一种疯狂的想象。
一种方法是创建一个多目标库,请注意 Visual Studio for Mac 还不喜欢这些项目,并且在那里创建和开发这样的库会遇到问题。但是,如果您只在 Windows Visual Studio 2017 及更高版本上进行开发,则可以很好地支持此类项目。
您可以从在 VS 中创建一个 .NET Standard 库开始,这将为您提供一个项目,其中包含一个包含 Class1 的 .cs 文件,然后将其删除。
如果您编辑 .csproj 文件,您将在文件顶部看到一个 PropertyGroup,其中包含一个名为 TargetFramework 的属性。将其更改为 TargetFrameworks 并列出您想要定位的所有所需框架:
<PropertyGroup
<TargetFrameworks>netstandard2.0;Xamarin.iOS10;MonoAndroid81;uap10.0.16299</TargetFrameworks>
如果您想在多目标库中获得轻松支持,我建议您为您的项目 SDK 切换到 MSBuildSDKExtras,因为它可以处理许多针对 Xamarin 平台的怪癖。它将为您的项目添加必要的属性以完成所有这些工作。
所以 .csproj 文件的第一行必须如下所示:
<Project Sdk="MSBuild.Sdk.Extras/1.6.68">
现在您需要指定您的平台特定代码所在的文件夹。我通常做的是创建一个带有android、ios 和uap 文件夹的platforms 文件夹并考虑其余代码独立于平台,驻留在common。
<ItemGroup>
<Compile Remove="platforms\**\*.cs" />
<None Include="platforms\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
<Compile Include="platforms\common\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
<Compile Include="platforms\ios\**\*.cs" />
<Compile Include="platforms\common\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
<Compile Include="platforms\android\**\*.cs" />
<Compile Include="platforms\common\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap')) ">
<Compile Include="platforms\uap\**\*.cs" />
<Compile Include="platforms\common\**\*.cs" />
</ItemGroup>
为了从这个多目标库中很好地创建 NuGet 包,我通常在解决方案的根目录中添加一个 Directory.build.props 文件,我在其中指定包的属性:
<Project>
<PropertyGroup>
<Company>Cheesebaron</Company>
<Copyright>Copyright © Cheesebaron</Copyright>
<RepositoryUrl>https://github.com/cheeesebaron/cool-stuff</RepositoryUrl>
<Authors>Cheesebaron</Authors>
<Owners>Cheesebaron</Owners>
<PackageReleaseNotes />
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryType>git</RepositoryType>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<NeutralLanguage>en</NeutralLanguage>
<LangVersion>latest</LangVersion>
<NoWarn>$(NoWarn);1591;1701;1702;1705;VSX1000;NU1603</NoWarn>
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' and '$(IsTestProject)' != 'true'">true</GeneratePackageOnBuild>
<Platform>AnyCPU</Platform>
<DebugType>portable</DebugType>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IsTestProject>$(MSBuildProjectName.Contains('UnitTests'))</IsTestProject>
</PropertyGroup>
</Project>
我还添加了一个Directory.build.targets 文件,用于定义不同平台的常量:
<Project>
<PropertyGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
<DefineConstants>$(DefineConstants);NETSTANDARD;PORTABLE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('Xamarin.iOS'))">
<DefineConstants>$(DefineConstants);MONO;UIKIT;COCOA;IOS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('MonoAndroid'))">
<DefineConstants>$(DefineConstants);MONO;ANDROID</DefineConstants>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
</PropertyGroup>
</Project>
如果您的目标是 Android,您可能还需要添加:
<None Include="Resources\*.cs" />
<Compile Remove="Resources\*.cs" />
到 .csproj 文件中的第一个 ItemGroup 并添加如下内容:
<AndroidResource Include="Resources\**\*.xml" SubType="Designer" Generator="MSBuild:UpdateAndroidResources" />
<AndroidResource Include="Resources\**\*.axml" SubType="Designer" Generator="MSBuild:UpdateAndroidResources" />
发送至 Android ItemGroup 以正确更新 Android 资源。
在 Visual Studio 中添加文件可能有点棘手,并且通常会将它们添加到前几个文件的错误位置,直到您在所有平台特定文件夹中都有内容。但是,只要将文件正确放置在我们定义的结构中,您就不需要 .csproj 文件中的任何其他内容。
您可以在此处查看多目标库的示例:
https://github.com/MvvmCross/MvvmCross/blob/develop/MvvmCross/MvvmCross.csproj
https://github.com/MvvmCross/MvvmCross/blob/develop/Directory.build.props
https://github.com/MvvmCross/MvvmCross/blob/develop/Directory.build.targets
对于 ReactiveUI 也是如此:
https://github.com/reactiveui/ReactiveUI/blob/master/src/ReactiveUI/ReactiveUI.csproj
到目前为止,它不是很容易上手,但很好用。
对此的替代方案是 .NET Standard 库和添加诱饵和切换模式的平台特定库。