[c#] 제네릭 형식 매개 변수에 대한 정적 메서드 호출

나는 이와 같은 일을하고 싶었지만 C #에서는 불법으로 보입니다.


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

컴파일 타임 오류가 발생합니다. ” ‘T’는 ‘유형 매개 변수’이며 지정된 컨텍스트에서 유효하지 않습니다.”

제네릭 형식 매개 변수가 주어지면 제네릭 클래스에서 정적 메서드를 어떻게 호출 할 수 있습니까? 제약 조건이 주어지면 정적 메서드를 사용할 수 있어야합니다.



답변

이 경우 제약 된 형식에 대해 직접 정적 메서드를 호출해야합니다. C # (및 CLR)은 가상 정적 메서드를 지원하지 않습니다. 그래서:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

… 다음과 다를 수 없습니다.

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

제네릭 유형 매개 변수를 거치는 것은 불필요한 간접적이므로 지원되지 않습니다.


답변

이전 답변에 대해 자세히 설명하기 위해 여기에서 원하는 것에 반성이 더 가깝다고 생각합니다. 나는 당신이 무언가를해야하거나하지 말아야하는 이유를 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을 구현하도록 강요하지 않으면 충돌 할 수 있음을 알고 있지만 질문에 대답합니다.


답변

이러한 메서드를 호출하는 유일한 방법은 리플렉션을 통하는 것입니다. 그러나 해당 기능을 인터페이스에 래핑하고 인스턴스 기반 IoC / 팩토리 / 등 패턴을 사용할 수있는 것처럼 들립니다.


답변

C #에 “가상 정적 메서드”가 없다는 사실을 해결하기 위해 제네릭을 사용하려는 것 같습니다.

불행히도 그것은 작동하지 않을 것입니다.


답변

지금은 할 수 없습니다. 당신은 컴파일러에게 T가 그 메소드를 가지고 있다는 것을 알려주는 방법이 필요합니다. 현재로서는 그렇게 할 방법이 없습니다. (많은 사람들이 일반 제약 조건에 지정할 수있는 것을 확장하도록 Microsoft를 추진하고 있으므로 향후 가능할 수도 있습니다.)


답변

여기에 작동하는 예제를 게시하면 해결 방법입니다.

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


답변

나는 상황에 따라 때때로 대리인이 이러한 문제를 해결하도록 그것을 버리고 싶었습니다.

어떤 종류의 팩토리 또는 초기화 메서드로 정적 메서드를 호출해야하는 경우 대리자를 선언하고 정적 메서드를 관련 제네릭 팩토리 또는이 “이 정적 메서드가있는 제네릭 클래스”가 필요한 모든 것에 전달할 수 있습니다.

예를 들면 :

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 메소드를 재사용하거나 인스턴스 메소드가 될 수 있습니다.