2013-03-20 5 views
10

개체를 저장하는 일반적인 메서드를 호출 할 때 간혹 특정 형식을 다르게 처리해야하는 경우가 있습니다. 나는 당신이 제약 조건에 따라 과부하가 될 수 없다는 것을 알고 있지만, 다른 대안은 그 자신의 문제를 제시하는 것 같습니다. 다음과 같이 우리의 팀은 이러한 클래스를 저장하기위한 '일회성'방법을 만들었습니다 과거일반 메서드 오버로드

public bool Save<SpecificClass>(T entity) 
{ ... special logic ... } 

: 내가 할 싶은 것이

public bool Save<T>(T entity) where T : class 
{ ... some storage logic ... } 

는 다음과 같은 것입니다

public bool SaveSpecificClass(SpecificClass sc) 
{ ... special logic ... } 

그러나 기능을 알고 있지 않고 일반 (저장)을 사용하려고하면 '일회용'이 수정되어야하는 여러 가지 문제가 발생할 수 있습니다. 새로운 개발자가 따라 와서 제네릭 문제를보고 악의적 인 개발자가 자신의 일회용 기능으로 수정하려고 결정하면 이는 더욱 악화 될 수 있습니다.

이렇게 ...

이 겉으로보기에는 일반적인 문제를 해결하기위한 옵션은 무엇입니까?

저는 UnitOfWork를 보았습니다. 지금은 인 것 같습니다. 옵션 만 실제로 문제를 해결하지만, 슬레지 해머로 날아가는 것처럼 보입니다.

+2

C++과 달리 C#은 템플릿 전문화를 허용하지 않습니다. –

+0

이러한 Save() 메소드는 일부 경량 헬퍼 클래스 또는 일부 엔터티 클래스에 있습니까? 나는 상속에 대해 생각하고 있지만 상속이 항상 올바르게 사용되지는 않기 때문에 이것이 올바른지 확인하는 것이 중요합니다. – sll

+0

가능한 중복 [C#의 템플릿 전문화 방법] (http://stackoverflow.com/questions/600978/how -to-do-template-specialization-in-c-sharp) –

답변

12

당신은 할 수 있습니다 : 예를 들어

public bool Save<T>(T entity) where T : class 
{ ... some storage logic ... } 

public bool Save(SpecificClass entity) 
{ ... special logic ... } 

을 :

public class SpecificClass 
{ 
} 

public class Specializer 
{ 
    public bool GenericCalled; 
    public bool SpecializedCalled; 

    public bool Save<T>(T entity) where T : class 
    { 
     GenericCalled = true; 
     return true; 
    } 

    public bool Save(SpecificClass entity) 
    { 
     SpecializedCalled = true; 
     return true; 
    } 
} 

public class Tests 
{ 
    [Test] 
    public void TestSpecialization() 
    { 
     var x = new Specializer(); 
     x.Save(new SpecificClass()); 
     Assert.IsTrue(x.SpecializedCalled); 
     Assert.IsFalse(x.GenericCalled); 
    } 
} 
+0

이것이 효과가있어,이 간단한 솔루션을 생각하지 못했다고 나는 믿을 수 없다 ... –

+0

나는 작동하도록 할 수 없었다. 함수의 버전은 어떤 경우에도 호출되었습니다. – MarkusParker

+0

@ MarkusParker 단위 테스트에서 작동하는 것으로 보이는 업데이트에 설명 된대로 잘 작동해야하므로 예제를 제공 할 수 있습니까? 'SpecificClass'를 전달하는 데 사용 된 변수는 컴파일 타임 다형성을 수행하는 '동적'클래스가 아닌 한 'SpecificClass' 유형이어야합니다. –

0

왜 당신의 방법에 대해 다른 이름을 사용하고 계십니까?

은 다음을 참조하십시오

public class Entity 
    { 
    } 

    public class SpecificEntity : Entity 
    { 
    } 

    public class Program 
    { 
     public static void Save<T>(T entity) 
      where T : class 
     { 
      Console.WriteLine(entity.GetType().FullName); 
     } 

     public static void Save(SpecificEntity entity) 
     { 
      Console.WriteLine(entity.GetType().FullName); 
     } 

     private static void Main(string[] args) 
     { 
      Save(new Entity());   // ConsoleApplication13.Entity 
      Save(new SpecificEntity()); // ConsoleApplication13.SpecificEntity 

      Console.ReadKey(); 
     } 
    } 
2

잘 basicly C 번호는 다음과 같이 상속을 통해 제외하고, 템플릿 특수화를 허용하지 않습니다

interface IFoo<T> { } 
class Bar { } 

class FooBar : IFoo<Bar> { } 

는 적어도 컴파일 시간 동안이 기능을 지원하지 않습니다. 그러나 당신은 당신이 달성하려고하는 일을 할 RTTI를 사용할 수 있습니다

public bool Save<T>(T entity) 
{ 
    // Check if "entity" is of type "SpecificClass" 
    if (entity is SpecificClass) 
    { 
     // Entity can be safely casted to "SpecificClass" 
     return SaveSpecificClass((SpecificClass)entity); 
    } 

    // ... other cases ... 
} 

is expression 런타임 유형 검사를 할 매우 편리합니다.

if (entity.GetType() == typeof(SpecificClass)) 
    // ... 

편집 : 그것은 다음 코드와 유사한 작동

if (entity is Foo) 
    return DoSomethingWithFoo((Foo)entity); 
else if (entity is Bar) 
    return DoSomethingWithBar((Bar)entity); 
else 
    throw new NotSupportedException(
     String.Format("\"{0}\" is not a supported type for this method.", entity.GetType())); 

편집 2 : 다른 답변이 제안으로 알 수없는 유형이 다음과 같은 패턴을 사용하는 것이 매우 일반적이다 메서드를 SpecializedClass과 함께 사용하면 다형성 작업을 할 때주의가 필요합니다.리포지토리에 인터페이스를 사용하는 경우 (실제로 리포지토리 패턴을 디자인하는 좋은 방법입니다) 오버로드하면 SpecializedClass의 개체를 전달하는 경우에도 잘못된 메서드가 호출되는 경우가 발생할 수 있습니다.

var repository = new FooRepository(); 
repository.Save(new Foo()); 

그러나 당신이 패턴을 사용하는 경우는 예 (인터페이스를 호출하는 경우이 작동하지 않습니다 인터페이스에 : 직접 Foo의 인스턴스와 FooRepository.Save를 호출하는 경우

interface IRepository 
{ 
    bool Save<T>(T entity) 
     where T : class; 
} 

class FooRepository : IRepository 
{ 
    bool Save<T>(T entity) 
    { 
    } 

    bool Save(Foo entity) 
    { 
    } 
} 

이 작동 저장소 생성 구현) :

IRepository repository = GetRepository<FooRepository>(); 
repository.Save(new Foo()); // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)! 

RTTI를 사용하면 Save 방법이 하나만 있어도 괜찮습니다.

+0

'Save (SpecificClass entity) '와 같은 특정 유형의 오버로드가 잘못되었습니다. –

+0

아무 문제가 없지만 여전히 전문화되지 않은 과부하입니다. ;) – Carsten

+0

질문 * "overloading"에 관한 것입니다 ... :) –

3

코드는 두 가지 방법이있는 경우 제네릭을 포함하는 기능과 연산자 오버로드는, 컴파일시에 바인드보다는 런타임 때문에 :

public bool Save<T>(T entity) ... 
public bool Save(SomeClass entity) ... 

호출하려고 다음 코드를 Foo 일부의 변수입니다 Save(Foo) 제네릭 형식이 SomeClass 일 때도 제네릭 형식은 항상 이전 오버로드를 호출합니다. 이를 해결하기위한 제 제안은 일반 인터페이스 ISaver<in T>을 비 제너릭 메서드 DoSave(T param)으로 정의하는 것입니다. Save 메소드를 제공하는 클래스가 처리 할 수있는 유형에 적합한 모든 일반 인터페이스를 구현하도록하십시오. 그런 다음 객체의 Save<T> 메서드를 thisISaver<T>으로 캐스팅합니다. 형 변환이 성공하면 결과로 ISaver<T>을 사용합니다. 그렇지 않으면 일반 저장을 수행하십시오. 클래스 형식 선언에 저장할 수있는 유형의 모든 적절한 인터페이스가 나열되어 있으면이 방법으로 Save 호출을 적절한 방법으로 전달합니다.

관련 문제