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


107

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


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

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

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


답변:


58

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

T.StaticMethodOnSomeBaseClassThatReturnsCollection

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

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

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


25
하지만 자식 클래스에서 정적 메서드를 마스킹했다면 어떻게 될까요? public class SomeChildClass : SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} 제네릭 형식에서 해당 정적 메서드에 액세스하기 위해 뭔가를 할 수 있습니까?
Hugo Migneron

2
아래 Joshua Pech의 답변을 확인하십시오. 그 경우에 효과가 있다고 생각합니다.
Remi Despres-Smyth 2011

1
시겠습니까 return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();일? 그렇다면 답변에 추가하고 싶을 수도 있습니다. 감사. 그것은 나를 위해 일했습니다. 제 경우에는 클래스에 유형 매개 변수가 있으므로 그렇게 return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();했습니다.
toddmo

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을 명시하는 것과 다르지 않습니다.
Remi Despres-Smyth 2011

2
이것은 정적 메서드와 함께 작동, 내가 필요한 것입니다
myro

이것은 내가 클래스와 기본 클래스를 제어 할 수 없었기 때문에 필요한 대답이었습니다.
Tim

8

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


5

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

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


1
나는 아닙니다. 생성 된 DAL 레이어 위에서 작업하고 있습니다. 생성 된 클래스는 모두 정적 FetchAll 메서드가있는 기본 클래스에서 상속됩니다. "일반"저장소 클래스를 사용하여 저장소 클래스에서 코드 중복을 줄이려고합니다. 사용 된 구체적인 클래스를 제외하고는 많은 반복 코드가 있습니다.
Remi Despres-Smyth

1
그렇다면 SomeBaseClass.StaticMethod ... ()를 호출하지 않는 이유는 무엇입니까?
Brad Wilson

죄송합니다. 이전 댓글에서 잘 설명하지 못했습니다. FetchAll은 기본에서 정의되지만 파생 클래스에서 구현됩니다. 파생 클래스에서 호출해야합니다.
Remi Despres-Smyth

7
정적 메서드 인 경우 기본에 의해 정의되고 구현됩니다. C #에는 가상 / 추상 정적 메서드와 같은 것이 없으며 이에 대한 재정의도 없습니다. 나는 당신이 단순히 그것을 다시 선언했다고 생각합니다. 이것은 매우 다릅니다.
Marc Gravell

1
네, 맞아요-여기서 잘못된 가정을했습니다. 토론 해 주셔서 감사합니다. 올바른 방향으로 나아가는 데 도움이되었습니다.
Remi Despres-Smyth

2

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


1
문제는 제네릭이 런타임에서 제공되기 때문에 아마도 2.0 이후로 (대부분) 피한 새로운 CLR 버전을 의미 할 수 있다는 것입니다. 그래도 우리는 새로운 것이 될 수 있습니다 ...
Marc Gravell

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 ()가 있으면 항상 (new T ()). someInstanceMethod (); -그러나 이것은 인스턴스 메소드를 호출하고 있습니다-질문은 클래스의 정적 메소드를 호출하는 방법을 물었습니다.
timothy

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.