Виклик статичного методу за загальним параметром типу


107

Я сподівався зробити щось подібне, але це здається незаконним у C #:


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

Я отримую помилку часу компіляції: "T" - це "параметр типу", який не є дійсним у заданому контексті. "

З огляду на параметр загального типу, як я можу викликати статичний метод на загальному класі? Статичний метод повинен бути доступним, враховуючи обмеження.


Відповіді:


58

У цьому випадку слід просто зателефонувати статичному методу на обмежений тип. C # (і CLR) не підтримують віртуальні статичні методи. Так:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... не може відрізнятися від:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Перехід через параметр загального типу - це непотрібна непрямість і, отже, не підтримується.


25
Але що робити, якщо ви замаскували свій статичний метод у дитячому класі? публічний клас SomeChildClass: SomeBaseClass {новий новий статичний StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Чи можете ви зробити щось для доступу до цього статичного методу з загального типу?
Уго Міньерон

2
Перевірте відповідь Джошуа Печа нижче, я вважаю, що це спрацювало б у такому випадку.
Ремі Деспрес-Сміт

1
Було б return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();працювати? Якщо так, ви можете додати це до своєї відповіді. Дякую. Це працювало для мене. У моєму випадку мій клас мав парам типу, тому я return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();і працював.
Тоддмо

27

Щоб детальніше розглянути попередню відповідь, я думаю, що рефлексія ближче до того, що ви хочете тут. Я міг би дати 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, але він відповідає на запитання як задано.


2
Ні, зовсім ні. JaredPar зрозумів це абсолютно правильно: T.StaticMethodOnSomeBaseClassThatReturnsCollection, де T: SomeBaseClass не відрізняється від того, щоб просто вказати SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection.
Ремі Депресс-Сміт

2
Це те, що мені було потрібно, воно працює статичним методом
myro

Ця відповідь мені була потрібна, оскільки я не мав контролю над класами та базовим класом.
Тім

8

Єдиним способом виклику такого методу було б за допомогою рефлексії, однак, це здається, що це можливо, щоб зафіксувати цю функціональність в інтерфейсі та використовувати шаблон на основі екземпляра IoC / factory / тощо.


5

Здається, що ви намагаєтесь використовувати дженерики, щоб подолати те, що в C # немає "віртуальних статичних методів".

На жаль, це не вийде.


1
Я не - я працюю над створеним шаром DAL. Усі згенеровані класи успадковують від базового класу, який має статичний метод FetchAll. Я намагаюся зменшити дублювання коду в моєму класі репозиторію з "загальним" класом сховища - багато повторюваного коду, за винятком конкретного використовуваного класу.
Ремі Деспрес-Сміт

1
Тоді чому б вам просто не зателефонувати SomeBaseClass.StaticMethod ... ()?
Бред Уілсон

Вибачте, я не пояснив себе добре в попередньому коментарі. FetchAll визначається на базі, але реалізується на похідних класах. Мені потрібно назвати це похідним класом.
Ремі Деспрес-Сміт

7
Якщо це статичний метод, то він визначається і реалізується базою. У C # немає такого поняття, як віртуальний / абстрактний статичний метод, і немає переопрацювання для нього. Я підозрюю, що ви її просто повторно заявили, що дуже відрізняється.
Марк Гравелл

1
Так, ви маєте рацію - я зробив тут недійсні припущення. Дякую за обговорення, це допомогло поставити мене на правильний шлях.
Ремі Деспрес-Сміт

2

На сьогодні ви не можете. Вам потрібен спосіб сказати компілятору, що T має цей метод, і в даний час, це не можливо зробити. (Багато хто підштовхує Microsoft розширити те, що може бути визначено загальним обмеженням, тому, можливо, це стане можливим у майбутньому).


1
Проблема полягає в тому, що оскільки дженерики забезпечуються під час виконання, це, мабуть, означатиме нову версію CLR, якої вони (значною мірою) уникали з 2.0. Можливо, нам належить новий, хоча ...
Марк Гравелл

2

Ось, я публікую приклад, що працює, це вирішення

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();
}

2
Це дає мені синтаксичну помилку? Що public T : SomeBaseClassозначає?
Ерік

Якщо у вашому класі є метод екземпляра someInstanceMethod (), ви завжди можете викликати це, виконуючи (новий T ()). - але це виклик методу екземпляра - питання задає питання, як викликати статичний метод класу.
тимофіївка

2

Мені просто хотілося це викинути, щоб іноді делегати вирішували ці проблеми залежно від контексту.

Якщо вам потрібно називати статичний метод як якийсь фабричний або ініціалізаційний метод, то ви можете оголосити делегата та передати статичний метод відповідному загальному заводу або будь-якому іншому, що потребує цього "загального класу за допомогою цього статичного методу".

Наприклад:

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();
    }
}

На жаль, ви не можете встановити, що клас має правильний метод, але ви можете принаймні призначати час компіляції, що в отриманому заводському методі є все, що очікується (тобто метод ініціалізації з точно правильною підписом). Це краще, ніж виняток відображення часу виконання.

Цей підхід також має деякі переваги, тобто ви можете повторно використовувати методи init, використовувати їх, наприклад, методи екземплярів тощо.


1

Ви повинні зробити це за допомогою рефлексії, як описано тут

Через те, що посилання загинула, я знайшов відповідні деталі в машині зворотного зв'язку:

Припустимо, у вас клас зі статичним загальним методом:

class ClassWithGenericStaticMethod
{
    public static void PrintName<T>(string prefix) where T : class
    {
        Console.WriteLine(prefix + " " + typeof(T).FullName);
    }
}

Як можна викликати цей метод, використовуючи релекцію?

Це виявляється дуже просто ... Ось як ви викликаєте статичний загальний метод за допомогою рефлексії:

// Grabbing the type that has the static generic method
Type typeofClassWithGenericStaticMethod = typeof(ClassWithGenericStaticMethod);

// Grabbing the specific static method
MethodInfo methodInfo = typeofClassWithGenericStaticMethod.GetMethod("PrintName", System.Reflection.BindingFlags.Static | BindingFlags.Public);

// Binding the method info to generic arguments
Type[] genericArguments = new Type[] { typeof(Program) };
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

// Simply invoking the method and passing parameters
// The null parameter is the object to call the method from. Since the method is
// static, pass null.
object returnValue = genericMethodInfo.Invoke(null, new object[] { "hello" });

Посилання мертва.
Necronomicron
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.