제네릭 형식의 특정 인스턴스로 C# 메서드를 오버로드하는 방법
-
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>
, 그리고 제작자를 전달해야합니다.