Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extension method call does not compile, but static method call to same code does compile

There is library A calling library B using a C# extension method.

I got a weird error from the C# compiler:

The type 'System.Windows.Forms.Control' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Windows.Forms, Version=4.0.0.0

None of the libraries A or B are dependent on System.Windows.Forms.Control, nor has any dependency of A a dependency on System.Windows.Forms.Control. System.Windows.Forms.Control is only referenced from another project in the same solution.

The weird thing is that if I change the call syntax to static method, it compiles successfully.

//static method syntax works fine
var leads = SourceLeadConfigurationHelper.GetLeads(enLeadSystem);

//extension method syntax cause error
//error The type 'System.Windows.Forms.Control' is defined in an assembly that is not referenced. 
var leads = enLeadSystem.GetLeads();

The extension method looks like:

public static class SourceLeadConfigurationHelper
{      
    public static IList<ChannelID> GetLeads(this LeadSystem leadSystem);
    public static IList<ChannelID> GetLeads(this SourceLeadConfiguration slc);
    public static IList<ChannelID> GetLeads(LeadSystem leadSystem, bool throwException);
}

So you see there is no problem with detecting which extension to use. LeadSystem is an enum, SourceLeadConfiguration is a class.

I have Visual Studio 2013 update 5, .NET Framework 4.0.

like image 229
Tomas Kubes Avatar asked Mar 09 '16 09:03

Tomas Kubes


1 Answers

This is a pretty consistent complaint about C# compiler behavior, drives programmers pretty nutty. Unfortunately there is no canonical Q+A for it, every case is different. The first reports of it started showing up around the VS2012/.NET 4.5 time frame. The compiler did not used to behave this way, it smells like a bug fix that had a bigger impact than anticipated. We also don't hear about it often enough, most programmers just follow the guidance in the error message and add the assembly reference. So should you, it trivially solves the problem.

Trying to characterize it a bit here. It doesn't directly have anything to do with extension methods, this behavior is specific to method overload resolution. Extension methods are just a special case of it, a tricky case for sure.

Finding a method overload match is not particularly tricky, it is generating a decent error message when the overload is ambiguous that is the problem. One thing that's quite clear about the changed behavior is that the compiler is now more thorough. It insists on knowing everything about the type of an argument. Even if it is crystal clear to programmer's eyes that the passed argument cannot possibly match a type in another unreferenced assembly. But the compiler is pig-headed about it, if it does not know the type then it insists you add the reference.

Extension methods are tricky because there might be many, not just the SourceLeadConfigurationHelper.GetLeads() one you (obviously) expected to be picked. An assembly might define other extension methods, and they just might add more extension methods to the SourceLeadConfiguration type. If the compiler knows the assembly exists, but does not know what it looks like, and thus can't know what extension methods it may contain, then it will complain. Note how this explains why you don't get the error when you call the static method directly, there is no need for the compiler to go look for extensions.

You can surely guess how the compiler got to find out about the System.Windows.Forms assembly. It got introduced by the other assembly, the one you mentioned but did not describe. The diagnostic is that the System.Windows.Forms.Control type leaked out in the metadata of that assembly. Usually as an argument or return type of a public method. You'll have to do some digging in the source code to find it, not otherwise a slamdunk that you can eliminate it.


One more gritty detail about extension methods that might be relevant here, the ExtensionAttribute type was moved in .NET 4.5 from System.Core.dll to mscorlib.dll. A rather heavy change and one that has caused lots of problems when programmers don't build their assemblies correctly. If you target .NET 4.0 then you'll want to double-check this, details are here.


Hopefully you can chase it down from the assembly that references Winforms. If not, or you can't eliminate the exposure, then adding the reference is really all it takes to keep the compiler happy.

like image 143
2 revs Avatar answered Oct 13 '22 20:10

2 revs