Why does a static method on a generic type require

2020-04-05 19:19发布

问题:

public class BinarySearchTree<T>
where T : IComparable<T>
{
    public static BinarySearchTree<char> InitializeSampleCharacterBST()
    {
        var bst = new BinarySearchTree<char>();

        bst.Insert('F');
        bst.Insert('B');
        bst.Insert('A');
        bst.Insert('D');
        bst.Insert('C');
        bst.Insert('G');
        bst.Insert('I');
        bst.Insert('H');

        return bst;
    }

class Program
{
        static void Main(string[] args)
        {
            var bst = BinarySearchTree.InitializeSampleCharacterBST();
        }
}

Why is this illegal? It's expecting me to provide a type parameter to the method call for the class which makes no sense. A generic class or method has no use for a type parameter in a static context.

It wants me to write the call like this:

var bst = BinarySearchTree<foo>.InitializeSampleCharacterBST();

Where foo can be any type I want regardless of the fact that the static method call returns a specifically typed generic object.

回答1:

the class BinarySearchTree and BinarySeachTree<Foo> are completely separate; the language allows generic type overloading. Perhaps declare this method on a non-generic twin class:

public static class BinarySearchTree {
    public static BinarySearchTree<char> InitializeSampleCharacterBST() {...}
}
public class BinarySearchTree<T> {...} // rest of the code

Otherwise... what T would it use? And what if the static method talked to static fields? Let alone which T to use, each T gets different static fields (i.e. SomeType<Foo> has separate fields to SomeType<Bar>).



回答2:

As Marc said, it's sometimes useful to overload the type to have a non-generic class - and it would be in this case.

As for why it's necessary, suppose that the static method were actually implemented as:

public static BinarySearchTree<char> InitializeSampleCharacterBST()
{
    Console.WriteLine(typeof(T));
    return null;
}

That would be perfectly valid code - it's in a generic type, so it should have access to the type parameter... but you're trying to call the method without providing a generic type parameter, so it couldn't possibly work. In your case you happen to not use T anywhere within the method, but that's a coincidence. It's a bit like having an instance method which doesn't use this: you're not using the instance, but you still can't call it as if it were a static method.

As well as having separate static classes, another design technique which can be useful is to split your type into non-generic and generic pieces. That way, in cases where it can be awkward to work out which exact type you have, you don't actually need to know it in order to call some of the members. For example, a collection interface hierarchy might have:

public interface ISomeCollection
{
    int Count { get; }
    void Clear();
}

public interface ISomeCollection<T> : ISomeCollection
{
    void Add(T item);
}

I've used this technique myself for my Protocol Buffers port to C#, and it's proved very useful (if somewhat complicated).



回答3:

You're forgetting that type parameters don't only appear in the parameter/return type of a method. They can also appear in the implementation:

public static BinarySearchTree<char> InitializeSampleCharacterBST()
{
    var forSomeReason = new T();

By placing your method inside a static class with a type parameter, you are saying that the implementation of the method may (now or in some future revision) depend upon that type parameter.

If this isn't the case, you've put the method in the wrong place.



回答4:

Because the type itself is Generic, you have to provide a type argument, even if the static method you are interested in does not make use of that type argument. Its just the nature of generics in C#...they don't exist in a non-generic form at any time. If they did, that would cause conflicts with a non-generic version of the same type.



标签: c# generics