如何在可重用的.NET库上启用向前兼容性?

时间:2013-08-01 14:43:29

标签: c# .net frameworks nuget framework-design

我正在创建我toy project的新次要版本。该项目在NuGet上发布,与.NET 4.0及更高版本兼容。我正在介绍的一些新功能需要.NET 4.5(用户应该能够解析IReadOnlyCollection<T>IReadOnlyList<T>,这两个接口都是在.NET 4.5中引入的),但我需要保留项目与.NET 4.0兼容,因为并非所有开发人员都可以轻松迁移到最新的.NET框架。

所以我面临的问题是如何解决这个“前向兼容性”问题。我想到了两种解决方案,但两者都不是很有吸引力,所以希望任何人都可以在这里给我一些想法或指导。

以下是我提出的两个解决方案:

解决方案1:使用#if编译器指令并根据.NET框架版本构建DLL,并使用NuGet包发布这些版本并在项目站点下载。

这种方法的缺点是当开发人员将他们的Visual Studio项目从.NET 4.0更新到.NET 4.5时,他们不会自动获得.NET 4.5版本(具有.NET 4.5特定功能)。这违反了Principle of least astonishment,并且当开发人员几个月后尝试使用该功能时,会让开发人员误认为该功能无效。

解决方案2:使用单个DLL并动态发出类型,当它们存在于当前应用程序域中时实现这两个新接口。这允许将单个DLL发送给用户,并允许在开发人员在其项目中切换.NET框架版本时提供功能。这将使事情'正常工作'。这是我目前的方向。

因为我需要返回一个需要实现接口的类型,所以缺点是必须在运行时使用Reflection.Emit,ModuleBuilder,TypeBuilder等创建该类型。这是非常讨厌的shizzle。但除此之外,由于这种类型必须在一个新的(匿名)程序集中创建,我必须将一些内部类型设为public(它需要继承的类型和它需要实现的接口)。使这些内部类型公开污染项目的API,并且不允许我对这些类型进行更改。

我相信这些是我的选择,但我可能会遗漏一些明显的东西。所以我的问题是,我错过了一个可能性吗?有没有办法绕过解决方案1的问题,还是更好地使用运行时类型发射的核心根?

2 个答案:

答案 0 :(得分:2)

您是否考虑过另一个包含缺失项目的自定义程序集?然后测试是否存在类型/方法(仅存在于.net 4.5中),如果存在,则加载程序集。

通过这种方式,你可以保持完全相同的方法和类,并且可以避免做出所有疯狂发射的痛苦(​​更不用说如果你发现自己做了那么多,你会采取的性能)。

答案 1 :(得分:1)

我有一个名为Dynamitey的项目,它允许您在运行时加载一个类型,并使用DLR调用它的静态方法和构造函数。除了大量反射或发射代码以加载不一定可用的api之外,这将不那么混乱。

dynamic bigIntType = new DynamicObjects.LateType("System.Numerics.BigInteger, System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

if (bigIntType.IsAvailable)
{

  var one = bigIntType.@new(1);
  var two = bigIntType.@new(2);

  Assert.IsFalse(one.IsEven);
  Assert.AreEqual(true, two.IsEven);

  var tParsed = bigIntType.Parse("4");

  Assert.AreEqual(true, tParsed.IsEven);
}

我还有一个名为ImpromptuInterface的项目,它将为可调用的对象周围的接口发出代理类型(也使用DLR)。

var targetType =Type.GetType("System.Collections.Generic.IReadOnlyList`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

var list = new List<string>{"lala", "la","lala"};
object readonlyList;
if(targetType != null){
    readonlyList = Impromptu.DynamicActLike(list, targetType);
}