Escape Catch-22 with extension attributes in .NET

2019-01-19 00:14发布

问题:

How can a single .NET assembly, targeting 2.0, 3.0, 3.5, 4.0, and 4.5 concurrently, support extension methods for both C# and VB.NET consumers?

The standard suggestion is to add this:

namespace System.Runtime.CompilerServices
{
  public sealed class ExtensionAttribute : Attribute { }
}

This the approach suggested by more than one Microsoft employee and was even featured in MSDN magazine. It's widely hailed by many bloggers as having 'no ill effects'.

Oh, except it will cause a compiler error from a VB.NET project targeting .NET 3.5 or higher.

The authors of Microsoft.Core.Scripting.dll figured it out, and changed 'public' to 'internal'.

namespace System.Runtime.CompilerServices
{
  internal sealed class ExtensionAttribute : Attribute { }
}

Which seemed to solve the VB compatibility issue.

So I trustingly used that approach for the latest version (3.2.1) of the widely-used ImageResizing.Net library.

But then, we start getting this compiler error (original report), more or less randomly, for certain users targeting .NET 3.5+.

Error 5 Missing compiler required member
'System.Runtime.CompilerServices.ExtensionAttribute..ctor'

Because the MSBuild/VisualStudio compiler apparently doesn't bother to look at scoping rules when resolving naming conflicts, and the order of assembly references plays a not-quite-docuemented role, I don't fully understand why and when this happens.

There are a few hacky workarounds, like changing the assembly namespace, recreating the project file, deleting/readding System.Core, and fiddling with the target version of the .NET framework. Unfortunately, none of those workarounds are 100% (except aliasing, but that's an unacceptable pain).

How can I fix this while

  1. Maintaining support for extension method use within the assembly,
  2. Maintaining support for .NET 2.0/3.0
  3. Not requiring multiple assemblies for each .NET framework version.

Or, is there a hotfix to make the compiler pay attention to scoping rules?

Related questions on SO that don't answer this question

  • C# Extension methods in .NET 2.0
  • Using Extension Methods with .NET Framework 2.0
  • strange warning about ExtensionAttribute
  • Ambigious reference for ExtensionAttribute when using Iron Python in Asp.Net
  • Should I support .NET 2.0?
  • Using extension methods in .NET 2.0?

回答1:

We ran into the same issue with IronPython. http://devhawk.net/2008/10/21/the-fifth-assembly/

We ended up moving our custom version of ExtensionAttribute to its own assembly. That way, customers could choose between referencing our custom ExtensionAttribute assembly or System.Core - but never both!

The other tricky thing is that you have to always deploy the ExtensionAttribute assembly - even if you don't reference it in your project. Your project assemblies that expose extension methods will have an assemblyref to that custom ExtensionAttribute assembly, so CLR will get upset if it can't be found.

Given the hard requirement of .NET 2.0 support, I would think the best bet would be to simply not use extension methods at all. I'm not familiar with the ImageResizer project, but it sounds like this was a recent change in ImageResizer. How feasible would it be to change the extension methods to traditional static methods? We actually thought about that for IronPython/DLR, but it wasn't feasible (We were merged with LINQ at that point and LINQ had made heavy use of extension methods for essentially its entire existence).