Вызов статического метода для параметра общего типа

Я надеялся сделать что-то подобное, но в С# он выглядит незаконным:


public Collection MethodThatFetchesSomething<T>()
    where T : SomeBaseClass
{
    return T.StaticMethodOnSomeBaseClassThatReturnsCollection();
}

Я получаю ошибку времени компиляции: "'T' - это параметр типа, который недопустим в данном контексте.

Учитывая параметр generic type, как я могу вызвать статический метод для универсального класса? Статический метод должен быть доступен с учетом ограничения.

Ответ 1

В этом случае вам нужно просто вызвать статический метод на ограниченном типе напрямую. С# (и CLR) не поддерживают виртуальные статические методы. Итак:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... может не отличаться:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Прохождение общего параметра типа является ненужным косвенным и, следовательно, не поддерживается.

Ответ 2

Чтобы уточнить предыдущий ответ, я думаю, что размышление ближе к тому, что вы хотите здесь. Я мог бы дать 1001 причины, почему вы должны или не должны что-то делать, я просто отвечу на ваш вопрос, как было задано. Я думаю, вы должны вызвать метод GetMethod для типа общего параметра и перейти оттуда. Например, для функции:

public void doSomething<T>() where T : someParent
{
    List<T> items=(List<T>)typeof(T).GetMethod("fetchAll").Invoke(null,new object[]{});
    //do something with items
}

Где T - любой класс, у которого есть статический метод fetchAll().

Да, я знаю, что это ужасно медленно и может произойти сбой, если someParent не заставляет все свои дочерние классы реализовывать fetchAll, но он отвечает на заданный вопрос.

Ответ 3

Единственный способ вызова такого метода - через отражение. Тем не менее, похоже, что можно было бы обернуть эту функциональность в интерфейсе и использовать шаблон IoC/ factory/etc на основе экземпляра.

Ответ 4

Похоже, вы пытаетесь использовать дженерики, чтобы обойти тот факт, что в С# нет "виртуальных статических методов".

К сожалению, это не сработает.

Ответ 5

На данный момент вы не можете. Вам нужно указать компилятору, что T имеет этот метод, и в настоящее время нет никакого способа сделать это. (Многие подталкивают Microsoft расширять то, что может быть указано в общем ограничении, поэтому, возможно, это будет возможно в будущем).

Ответ 6

Здесь я пишу пример, который работает, это обходное решение

public interface eInterface {
    void MethodOnSomeBaseClassThatReturnsCollection();
}

public T:SomeBaseClass, eInterface {

   public void MethodOnSomeBaseClassThatReturnsCollection() 
   { StaticMethodOnSomeBaseClassThatReturnsCollection() }

}

public Collection MethodThatFetchesSomething<T>() where T : SomeBaseClass, eInterface
{ 
   return ((eInterface)(new T()).StaticMethodOnSomeBaseClassThatReturnsCollection();
}

Ответ 7

Вы должны иметь возможность сделать это с помощью отражения, как описано здесь

Ответ 8

Я просто хотел выбросить его там, что иногда делегаты решают эти проблемы, в зависимости от контекста.

Если вам нужно вызвать статический метод как своего рода метод factory или инициализации, то вы можете объявить делегат и передать статический метод соответствующему универсальному factory или тому, что ему нужно, это "общий класс с помощью этого статического метода".

Например:

class Factory<TProduct> where TProduct : new()
{
    public delegate void ProductInitializationMethod(TProduct newProduct);


    private ProductInitializationMethod m_ProductInitializationMethod;


    public Factory(ProductInitializationMethod p_ProductInitializationMethod)
    {
        m_ProductInitializationMethod = p_ProductInitializationMethod;
    }

    public TProduct CreateProduct()
    {
        var prod = new TProduct();
        m_ProductInitializationMethod(prod);
        return prod;
    }
}

class ProductA
{
    public static void InitializeProduct(ProductA newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class ProductB
{
    public static void InitializeProduct(ProductB newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class GenericAndDelegateTest
{
    public static void Main()
    {
        var factoryA = new Factory<ProductA>(ProductA.InitializeProduct);
        var factoryB = new Factory<ProductB>(ProductB.InitializeProduct);

        ProductA prodA = factoryA.CreateProduct();
        ProductB prodB = factoryB.CreateProduct();
    }
}

К сожалению, вы не можете обеспечить, чтобы класс имел правильный метод, но вы, по крайней мере, можете скомпилировать время, чтобы полученный метод factory имел все, что он ожидает (т.е. метод инициализации с точно подобранной сигнатурой). Это лучше, чем исключение отражения времени выполнения.

Этот подход также имеет некоторые преимущества, то есть вы можете повторно использовать методы init, использовать их как методы экземпляра и т.д.