开发者

Why is it impossible to declare extension methods in a generic static class?

开发者 https://www.devze.com 2022-12-26 04:38 出处:网络
I\'d like to create a lot of extension methods for some generic class, e.g. for public class SimpleLinkedList<T> where T:IComparable

I'd like to create a lot of extension methods for some generic class, e.g. for

public class SimpleLinkedList<T> where T:IComparable

And I've started creating methods like this:

public static class LinkedListExtensions
{
    public static T[] ToArray<T>(this SimpleLinkedList<T> simpleLinkedList) where T:IComparable
    {
       //// code
    }
}

But when I tried to make LinkedListExtensions class generic like this:

public static class LinkedListExtensions<T> where T:IComparable
{
    public static T[] ToArray(this SimpleLinkedList<T> simpleLinkedList)
    {
         ////code
    }
}

I get "Extension methods can only be declared in non-generic, non-nested static class".

And I'm trying to guess where this restriction came from and have no开发者_如何学C ideas.

EDIT: Still don't have clear vision of the problem. It seems like this was just not implemented for some reason.


Generally speaking, since you do not specify the class when you use an extension method, the compiler would have no way to know which is the class where the extension method is defined:

static class GenStatic<T>
{
  static void ExtMeth(this Class c) {/*...*/}
}

Class c = new Class();
c.ExtMeth(); // Equivalent to GenStatic<T>.ExtMeth(c); what is T?

Since extension methods themselves can be generic, this is no real problem at all:

static class NonGenStatic
{
  static void GenExtMeth<T>(this Class c) {/*...*/}
}

Class c = newClass();
c.ExtMeth<Class2>(); // Equivalent to NonGenStatic.ExtMeth<Class2>(c); OK

You can easily rewrite your example so that the static class is not generic, but the generic methods are. In fact, this is how .NET classes such as Enumerable are written.

  public static class LinkedListExtensions
  {
    public static T[] ToArray<T>(this SimpleLinkedList<T> where T:IComparable simpleLinkedList)
    {
      // code
    }
  }


The problem is how does the compiler do the extension resolution?

Say you define both the methods you describe:

public static class LinkedListExtensions {
    public static T[] ToArray<T>(this SimpleLinkedList<T> simpleLinkedList) where T:IComparable {
        //// code
    }
}
public static class LinkedListExtensions<T> where T:IComparable {
    public static T[] ToArray(this SimpleLinkedList<T> simpleLinkedList) {
        ////code
    }
}

Which method is used in the following case?

SimpleLinkedList<int> data = new SimpleLinkedList<int>();
int[] dataArray = data.ToArray();

My guess is that the language designers decided to restrict extension methods to the non-generic types to avoid this scenario.


A very interesting question, I have never been tempted to use static generic classes, but at least it seems possible.

In the context of declaring extension methods, you cannot only declare extension methods for a certain generic type (such as IEnumerable<T>) but also bring the type parameter T into the equation. If we agree to treat IEnumerable<int> and IEnumerable<string> as different types, this makes sense on a conceptual level too.

Being able to declare your extension methods in a static generic class would save you repeating your type parameter constraints over and over again, in effect grouping all extension methods for IEnumerable<T> where T : IComparable together.

According to the specification (citation needed), extension methods can only be declared in static not-nested and not-generic classes. The reason for the first two constraints are fairly obvious:

  1. May not carry any state as it's not a mixin, just syntactic sugar.
  2. Must have the same lexical scope as the type it provides extensions for.

The restriction on non-generic static classes seems a little arbitrary to me, I can't come up with a technical reason here. But it might be that the language designers decided to discourage you in writing extension methods that depend on the type parameter of a generic class you wish to provide extension methods for. Instead they want you to provide a truly generic implementation of your extension method but make it possible to provide optimized/specialized (compile-time bound) versions of your extension methods in addition to your general implementation.

Reminds me of template specialization in C++. EDIT: Unfortunately this is wrong, please see my additions below.


Ok, as this is a really interesting topic I did some further research. There actually is a technical restriction that I missed here. Let's look at some code:

public static class Test
{
    public static void DoSomething<T>(this IEnumerable<T> source)
    {
        Console.WriteLine("general");
    }
    public static void DoSomething<T>(this IEnumerable<T> source) where T :IMyInterface
    {
        Console.WriteLine("specific");
    }
}

This will actually fail with this compiler error:

Type 'ConsoleApplication1.Test' already defines a member called 'DoSomething' with the same parameter types

Ok, next we try splitting it up into two different extensions classes:

public interface IMyInterface { }
public class SomeType : IMyInterface {}

public static class TestSpecific
{
    public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface 
    {
        Console.WriteLine("specific");
    }
}
public static class TestGeneral
{
    public static void DoSomething<T>(this IEnumerable<T> source)
    {
        Console.WriteLine("general");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var general = new List<int>();
        var specific = new List<SomeType>();

        general.DoSomething();
        specific.DoSomething();

        Console.ReadLine();
    }
}

Against my initial impression (it was late yesterday night), this will result in an ambiguity at the call sites. To resolve this ambiguity one would need to call the extension method in a traditional way, but that's against our intention.

So this leaves us with a situation where it's not possible to declare compile-time bound generic specializations of extension methods. On the other hand, there is still no reason why we couldn't declare extension methods only for a single special generic type parameter. Therefore it'd be nice to declare them in a static generic class.

On the other hand writing an extension method like:

    public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface {}

or

    public static void DoSomething(this IEnumerable<IMyInterface> source) {}

is not all too different and only requires a little casting on the call site vs. on the extension method side (you will probably implement some optimization that depends on the specific type, so you'd need to cast T to IMyInterface or whatever anyway). So the only reason I can come up with is again, the language designers want to encourage you in writing generic extensions only in a truly generic fashion.

Some interesting things could happen here if we take co/contravariance in to the equation, which are about to be introduced with C# 4.0.


Don't think of extension methods as being tied to the static class in which they are contained. Instead, think of them as being tied to a specific namespace. Therefore, the static class in which they are defined is simply a shell used to declare these methods within the namespace. And while you could very well write multiple classes for different types of extension methods, you shouldn't think of the class itself as anything more than a way to clearly group your extension methods.

Extension methods do not extend any attributes of the class in which they are contained. The signature of the method will define everything about the extension method. And while you could also call your method like this, LinkedListExtensions.ToArray(...), I do not believe that was the intent for extension methods. As such, I believe the framework creators probably created the restriction you ran into as a way to inform developers simply that extension methods are self-contained and are not directly tied to the class in which they reside.


Just a thought, but is there any reason you can't just derive from this class and add the extra methods to the specialization rather than writing a suite of extension methods? I am sure you have your reasons but just throwing that out there.


Static classes should be able to be defined as abstract. If they were able to do this, then you could specify that they could not be used directly, but must be inherited just like a regular class and then generic static classes would be doable for extension methods because you could define them in the abstract class, inherit from said class, and everything would work properly.

As it is right now, there are a ton of significant limitations with static classes, this just being one that really messes with the mojo in a lot of cases. Combined with the inability to have the compiler infer the return type and thus requiring you to enter the whole generic implementation to call generic functions that don't have all of the generics in the method signature makes this stuff aggravatingly weak and makes for a lot of typing that shouldn't need to be there.

(There is also the case where the second generic is defined in the definition of the first and it won't use it, even though it's obvious to infer as well at compile time. This one is super annoying because it is completely obvious.) MS has a TON of work on Generics that they could be doing to improve this stuff.

0

精彩评论

暂无评论...
验证码 换一张
取 消