C #에서 템플릿 전문화를 수행하는 방법
C # 전문화는 어떻게 하시겠습니까?
문제를 제기하겠습니다. 템플릿 유형이 있지만 그것이 무엇인지 모릅니다. 그러나 XYZ
호출하려는 것에서 파생 된 것인지 알고 있습니다 .alternativeFunc()
. 가장 좋은 방법은 전문 함수 나 클래스를 호출하는 것입니다 normalCall
반환 .normalFunc()
의 파생 유형에 다른 전문성을 가지고있는 동안 XYZ
전화로 .alternativeFunc()
. 이것이 C #에서 어떻게 수행됩니까?
C #에서 전문화에 가장 가까운 방법은보다 구체적인 오버로드를 사용하는 것입니다. 그러나 이것은 부서지기 쉬우 며 가능한 모든 사용을 포함하지는 않습니다. 예를 들면 :
void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}
여기서 컴파일러가 컴파일 할 때 유형을 알고 있으면 가장 구체적인 유형을 선택합니다.
Bar bar = new Bar();
Foo(bar); // uses the specialized method
하나....
void Test<TSomething>(TSomething value) {
Foo(value);
}
이것은 컴파일 타임에 버닝되므로 Foo<T>
에도 사용 TSomething=Bar
됩니다.
한 가지 다른 접근 방식은 제네릭 메서드 내 에서 형식 테스트를 사용 하는 것입니다. 그러나 이것은 일반적으로 좋지 않은 생각이며 권장되지 않습니다.
기본적으로 C #은 다형성을 제외하고는 전문화 작업을 원하지 않습니다.
class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}
여기에서는 Bar.Foo
항상 올바른 재정의로 해결됩니다.
C ++ 템플릿으로 수행 할 수있는 템플릿 전문화에 대해 이야기하고 있다고 가정합니다. 이와 같은 기능은 실제로 C #에서 사용할 수 없습니다. 이는 C # 제네릭이 컴파일 중에 처리되지 않고 런타임의 기능이기 때문입니다.
그러나 C # 3.0 확장 메서드를 사용하여 유사한 효과를 얻을 수 있습니다. 다음은 MyClass<int>
템플릿 전문화와 마찬가지로 유형에 대해서만 확장 메소드를 추가하는 방법을 보여주는 예 입니다. 그러나 C # 컴파일러는 항상 확장 메서드보다 표준 메서드를 선호하므로이 메서드를 사용하여 메서드의 기본 구현을 숨길 수는 없습니다.
class MyClass<T> {
public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
public static int Bar(this MyClass<int> cls) {
return cls.Foo + 20;
}
}
이제 다음과 같이 작성할 수 있습니다.
var cls = new MyClass<int>();
cls.Bar();
전문화가 제공되지 않을 때 사용되는 메서드에 대한 기본 사례를 원한다면 일반 Bar
확장 메서드를 작성 하는 것이 트릭을 수행해야한다고 생각합니다.
public static int Bar<T>(this MyClass<T> cls) {
return cls.Foo + 42;
}
중간 클래스와 사전을 추가하여 전문화가 가능합니다 .
T를 전문화하기 위해 (예) Apply라는 메서드가있는 일반 인터페이스를 만듭니다. 인터페이스가 구현되는 특정 클래스의 경우 해당 클래스에 특정한 Apply 메서드를 정의합니다. 이 중간 클래스를 특성 클래스라고합니다.
해당 특성 클래스는 제네릭 메서드 호출에서 매개 변수로 지정 될 수 있으며, 그러면 (물론) 항상 올바른 구현을 취합니다.
수동으로 지정하는 대신 traits 클래스를 global에 저장할 수도 있습니다 IDictionary<System.Type, object>
. 그런 다음 검색하고 짜잔, 당신은 거기에 진정한 전문화가 있습니다.
편리한 경우 확장 메서드에 노출 할 수 있습니다.
class MyClass<T>
{
public string Foo() { return "MyClass"; }
}
interface BaseTraits<T>
{
string Apply(T cls);
}
class IntTraits : BaseTraits<MyClass<int>>
{
public string Apply(MyClass<int> cls)
{
return cls.Foo() + " i";
}
}
class DoubleTraits : BaseTraits<MyClass<double>>
{
public string Apply(MyClass<double> cls)
{
return cls.Foo() + " d";
}
}
// Somewhere in a (static) class:
public static IDictionary<Type, object> register;
register = new Dictionary<Type, object>();
register[typeof(MyClass<int>)] = new IntTraits();
register[typeof(MyClass<double>)] = new DoubleTraits();
public static string Bar<T>(this T obj)
{
BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>;
return traits.Apply(obj);
}
var cls1 = new MyClass<int>();
var cls2 = new MyClass<double>();
string id = cls1.Bar();
string dd = cls2.Bar();
자세한 설명과 샘플은 최근 블로그 링크 와 후속 조치를 참조하십시오 .
템플릿 전문화를 시뮬레이션하기위한 패턴도 찾고있었습니다. 일부 상황에서 작동 할 수있는 몇 가지 접근 방식이 있습니다. 그러나 사건은 어떻습니까
static void Add<T>(T value1, T value2)
{
//add the 2 numeric values
}
예를 들어 문을 사용하여 작업을 선택할 수 있습니다 if (typeof(T) == typeof(int))
. 그러나 단일 가상 함수 호출의 오버 헤드로 실제 템플릿 전문화를 시뮬레이션하는 더 좋은 방법이 있습니다.
public interface IMath<T>
{
T Add(T value1, T value2);
}
public class Math<T> : IMath<T>
{
public static readonly IMath<T> P = Math.P as IMath<T> ?? new Math<T>();
//default implementation
T IMath<T>.Add(T value1, T value2)
{
throw new NotSupportedException();
}
}
class Math : IMath<int>, IMath<double>
{
public static Math P = new Math();
//specialized for int
int IMath<int>.Add(int value1, int value2)
{
return value1 + value2;
}
//specialized for double
double IMath<double>.Add(double value1, double value2)
{
return value1 + value2;
}
}
이제 유형을 미리 알 필요없이 작성할 수 있습니다.
static T Add<T>(T value1, T value2)
{
return Math<T>.P.Add(value1, value2);
}
private static void Main(string[] args)
{
var result1 = Add(1, 2);
var result2 = Add(1.5, 2.5);
return;
}
구현 된 유형뿐만 아니라 파생 된 유형에 대해 특수화를 호출해야하는 경우 In
인터페이스에 대한 매개 변수를 사용할 수 있습니다 . 그러나이 경우 메서드의 반환 유형은 T
더 이상 제네릭 유형이 될 수 없습니다 .
제안 된 답변 중 일부는 런타임 유형 정보를 사용하고 있습니다. 본질적으로 컴파일 타임 바운드 메서드 호출보다 느립니다.
Compiler does not enforce specialization as well as it does in C++.
I would recommend looking at PostSharp for a way to inject code after the usual compiler is done to achieve an effect similar to C++.
I think there is a way to achieve it with .NET 4+ using dynamic resolution:
static class Converter<T>
{
public static string Convert(T data)
{
return Convert((dynamic)data);
}
private static string Convert(Int16 data) => $"Int16 {data}";
private static string Convert(UInt16 data) => $"UInt16 {data}";
private static string Convert(Int32 data) => $"Int32 {data}";
private static string Convert(UInt32 data) => $"UInt32 {data}";
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Converter<Int16>.Convert(-1));
Console.WriteLine(Converter<UInt16>.Convert(1));
Console.WriteLine(Converter<Int32>.Convert(-1));
Console.WriteLine(Converter<UInt32>.Convert(1));
}
}
Output:
Int16 -1
UInt16 1
Int32 -1
UInt32 1
Which shows that a different implementation is called for different types.
If you just want to test if a type is derrived from XYZ, then you can use:
theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));
If so, you can cast "theunknownobject" to XYZ and invoke alternativeFunc() like this:
XYZ xyzObject = (XYZ)theunknownobject;
xyzObject.alternativeFunc();
Hope this helps.
참고URL : https://stackoverflow.com/questions/600978/how-to-do-template-specialization-in-c-sharp
'developer tip' 카테고리의 다른 글
Ruby에서 문자열을 분할하고 첫 번째 항목을 제외한 모든 항목을 가져 오는 방법은 무엇입니까? (0) | 2020.10.07 |
---|---|
Java Byte [] 배열을 비교하는 방법은 무엇입니까? (0) | 2020.10.07 |
mingw-gcc 컴파일 된 실행 파일에 아이콘을 어떻게 추가합니까? (0) | 2020.10.07 |
CSS에서 "캐스 케이 딩"의 의미는 무엇입니까? (0) | 2020.10.07 |
Python-함수를 다른 함수로 전달 (0) | 2020.10.07 |