제네릭 형식의 특정 인스턴스로 C# 메서드를 오버로드하는 방법

StackOverflow https://stackoverflow.com/questions/405429

  •  03-07-2019
  •  | 
  •  

문제

C++ 배경 지식을 갖고 있는 저는 일반 유형의 특정 인스턴스를 기반으로 한 오버로드 문제에 부딪혔습니다.다음은 코드 인스턴스가 한 번만 작동하므로 작동하지 않습니다. Foo<T> 클래스는 항상 생성되므로 내부에는 Method, 유형 this 단순히 Foo<T>, 아니다 Foo<A> 또는 Foo<B> 내가 바라던 대로.C++에서는 고유한 유형으로 인스턴스화되는 템플릿에 익숙합니다.

using System.Collections.Generic;
class A
{
    // Concrete class
}

class B
{
    // Concrete class
}

class Bar
{
    public void OverloadedMethod(Foo<A> a) {} // do some A related stuff
    public void OverloadedMethod(Foo<B> b) {} // do some B related stuff
    public void OverloadedMethod(OtherFoo of) {} // do some other stuff

     public void VisitFoo(FooBase fb) { fb.Method(this); }
}

abstract class FooBase
{
    public abstract void Method(Bar b);
}

class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

class OtherFoo : FooBase
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<FooBase> ListOfFoos = new List<FooBase>();
        ListOfFoos.Add(new OtherFoo());
        ListOfFoos.Add(new Foo<A>());
        ListOfFoos.Add(new Foo<B>());

        Bar b = new Bar();
        foreach (FooBase fb in ListOfFoos)
            b.VisitFoo(fb);
        // Hopefully call each of the Bar::Overloaded methods
    }
}

C#에서 이와 같은 작업을 수행할 수 있는 방법이 있습니까?Foo에서 사용하려는 모든 유형에 대해 별도의 클래스로 코드를 복제할 필요가 없습니다.

편집하다:이것이 조금 더 명확해지기를 바랍니다.

도움이 되었습니까?

해결책

이것은 정적 케이스에 적용됩니다. 인스턴스 기능을 다루는 것이 조금 더 복잡합니다. 이것 Jon Skeet에서 게시하십시오 인스턴스 방법을 처리하는 합리적인 방법을 제공 할 수 있습니다.

class Program
{
    static void Main(string[] args)
    {
        var testA = new Foo<A>();
        testA.Method();
        var testB = new Foo<B>();
        testB.Method();
        Console.ReadLine();
        var testString = new Foo<string>(); //Fails
        testString.Method(); 
        Console.ReadLine();
    }
}

class A { }
class B { }
class Bar
{
    public static void OverloadedMethod(Foo<A> a)
    {
        Console.WriteLine("A");
    }
    public static void OverloadedMethod(Foo<B> b)
    {
        Console.WriteLine("B");
    }
}
class Foo<T>
{
    static Foo()
    {
        overloaded = (Action<Foo<T>>)Delegate.CreateDelegate(typeof(Action<Foo<T>>), typeof(Bar).GetMethod("OverloadedMethod", new Type[] { typeof(Foo<T>) }));
    }

    public void Method()
    {
        overloaded(this);
    }

    private static readonly Action<Foo<T>> overloaded;
}

다른 팁

이제 문제를 보여주는 완전히 완전한 코드 조각이 생겼습니다.OP 참고 사항:게시하기 전에 코드를 컴파일해 보세요.여기까지 오기 위해 내가 해야 할 일이 많았다.다른 사람들이 당신을 도울 수 있도록 가능한 한 쉽게 만드는 것이 좋습니다.불필요한 부분도 많이 제거했습니다.OtherFoo는 여기서는 실제로 관련이 없으며 FooBase도 마찬가지입니다.

class A {}
class B {}

class Bar
{
    public static void OverloadedMethod(Foo<A> a) { }
    public static void OverloadedMethod(Foo<B> b) { }
}

class Foo<T>
{
    // Class that deals with As and Bs in an identical fashion.
    public void Method()
    {
        // Doesn't compile here
        Bar.OverloadedMethod(this);
    }
}

예, 컴파일되지 않습니다.정확히 무엇을 기대했습니까?과부하 해결은 다음에서 수행된다는 점을 명심하십시오. 컴파일 시간, 실행 시간이 아닙니다.Fallen888이 말했듯이 적절한 오버로드된 메서드를 캐스팅하고 호출할 수 있습니다. 그러나 두 오버로드 중 컴파일러가 다르게 선택할 것으로 예상되는 것은 무엇입니까?그걸로 무엇을 하고 싶나요? Foo<string> 대신에 Foo<A> 또는 Foo<B>?

이 모든 것은 .NET 제네릭이 실제로 C++ 템플릿과 크게 다르다는 것을 보여줍니다. 물론...

나는 그것을 시도하지 않았지만 A & B를 방문 할 수있게함으로써 원하는 것을 달성 할 수 있어야합니다 (예 : acyclic visitor 패턴으로).

편집 : 시도 할 때이 작업을 완료 할 수 있는지 잘 모르겠습니다. 나는 이것을 작동시키기 위해 모든 종류의 트릭을 시도했고 그것을 컴파일 할 수는 없습니다. 내가 할 수있는 최선은 제네릭 클래스 밖에서 메소드 호출을 가져 오는 것입니다. 메소드 호출이 외부에 있으면 일반적인 t가 일반적인 것을 정의 할 수 있습니다. 그러나 메소드 내부에서 컴파일 시간에 컴파일러는 어떤 t가 무엇인지 알지 못하므로 어떤 오버로드 된 방법을 알지 못합니다. 이 주위에서 볼 수있는 유일한 방법은 스위치를 사용하여 T 유형을 결정하고 호출 할 과부하를 수동으로 지정하는 것입니다.

내가 할 수있는 최선은 이것입니다. 이것은 당신이 뒤 따르는 것이 아니라 비슷한 효과에 사용될 수 있습니다.

class Stuff<T>
{
    public T value { get; set; }
}

class Program
{
    static void DummyFunc(Stuff<int> inst)
    {
        Console.WriteLine("Stuff<int>: {0}", inst.value.ToString());
    }

    static void DummyFunc(Stuff<string> inst)
    {
        Console.WriteLine("Stuff<string>: {0}", inst.value);
    }
    static void DummyFunc(int value)
    {
        Console.WriteLine("int: {0}", value.ToString());
    }
    static void DummyFunc(string value)
    {
        Console.WriteLine("string: {0}", value);
    }
    static void Main(string[] args)
    {
        var a = new Stuff<string>();
        a.value = "HelloWorld";

        var b = new Stuff<int>();
        b.value = 1;

        var c = "HelloWorld";
        var d = 1;

        DummyFunc(a);
        DummyFunc(b);
        DummyFunc(c);
        DummyFunc(d);
    }
}

그리고 출력 :

Stuff<string>: HelloWorld
Stuff<int>: 1
string: HelloWorld
int: 1

2 개의 참조 일반 클래스 (int 및 String 용 1 개)와 2 개의 참조 규칙 유형 (int 및 String 용 1 개)을 참조하는 4 개의 과부하 기능이 있습니다. ?

편집 : 문제는 과부하 된 메소드의 호출과 관련이없는 것 같습니다. 과부하 된 메소드를 참조하기 위해 목록의 모든 항목을 첫 번째 유형으로 변환하려는 Foreach와 관련이 있습니다. 정확한 정의를 준수하지 않는 첫 번째 항목은 컴파일이 실패하게됩니다.

나는 이것을 할 수있는 더 쉬운 방법을 찾고 싶었지만 지금은 이것으로 갈 것입니다.

바꾸다 Foo<T> 이 수업과 함께 수업 :

abstract class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
}

class Foo_A : Foo<A>
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Foo_B : Foo<B>
{
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

인스턴스화를 바꾸십시오

List<FooBase> ListOfFoos = new List<FooBase>();
ListOfFoos.Add(new OtherFoo());
ListOfFoos.Add(new Foo_A());
ListOfFoos.Add(new Foo_B());

이것은 적어도 코드를 제한하는 것이 필요하지 않습니다 Foo<T>, 그리고 제작자를 전달해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top