2014-09-30 3 views
0

У меня есть базовый класс, какC#: Сделайте экземпляр подкласса

abstract class A <TEntity, TLog> 
{ 
    public abstract TLog Get(TEntity entity); 
} 

и несколько подклассов, как

class B : A<Branch, BranchLog> 
{ 
    public override BranchLog Get(Branch entity) 
    { 
     return null; 
    } 
} 

Теперь я могу получить экземпляр подкласса в зависимости от его общей реализации базовый класс, как,

class Mapper 
{ 
    public TLog GetMapped<TEntity, TLog>(TEntity entity) 
    { 

     /*if called with GetMapped<Branch, BranchLog>, make instance of B b = new B()*/ 
     /*returng b.Get(entity)*/ 
     return null; 
    } 
} 

или каким-либо образом, а не отражение

+0

без отражения? используя 'as' и' is'? – Noctis

+0

@Dipon: что вы на самом деле пытаетесь сделать здесь? Как клиенты Mapper-класса на самом деле используют ваш класс Mapper? –

+0

Самый эффективный способ - с отражением. – Enigmativity

ответ

3

Добавьте еще один общий параметр, указывающий тип. Ограничьте новый общий параметр new(), чтобы мы могли его обновить и A<TEntity, TLog>, чтобы убедиться, что могут быть переданы только экземпляры A или его подклассы.

class Mapper 
{ 
    public TLog GetMapped<TType, TEntity, TLog>(TEntity entity) where TType : A<TEntity, TLog>, new() 
    { 
     TType instance = new TType(); 
     return instance.Get(entity); 
    } 
} 

Тогда называют его

Mapper mapper = new Mapper(); 
mapper.GetMapped<B, Branch, BranchLog>(branch); 
+0

Мне жаль, что существует способ вызвать только 'GetMapped ' и дать компилятору вывод' TEntity' и 'TLog' ... У меня была мечта. – MarcinJuraszek

+0

@MarcinJuraszek Да, это всегда разочаровывает меня в том, что компилятор не может этого сделать. Я думаю, что они не реализовали эту функцию, потому что вы можете реализовать несколько универсальных интерфейсов. Таким образом, будет неоднозначность в выводе. Для абстрактных классов они могли бы иметь. –

+0

Они все равно могут реализовать его таким образом, чтобы вы могли сделать это, когда есть одно возможное разрешение, а также бросок и ошибка, когда это неоднозначно. – MarcinJuraszek

1

или каким-либо образом, а не отражение

уверен. Старый стиль if заявление:

public TLog GetMapped<TEntity, TLog>(TEntity entity) 
{ 
    if(typeof(TEntity) == typeof(Branch) && typeof(TLog) == typeof(BranchLog)) 
    { 
     B b = new B(); 
     return (TLog)b.Get((Branch)entity); 
    } 
    return default(TLog); 
} 

Но это не выглядит как правильное использование генериков или наследования. Вы уверены, что не пытаетесь решить проблему XY problem?

+0

@MatthewHaugen Вы правы. Кроме того, 'null' должен, вероятно, быть' default (TLog) '. – MarcinJuraszek

+0

Я согласен с утверждением, что это похоже на проблему XY. Надеемся, что этот вопрос может быть дополнительно уточнен, если будет решена актуальная проблема проектирования. –

-1

Ну, вы МОЖЕТЕ сделать это без refelction, но вам нужно знать все возможные типы, которые могут быть сделаны для TEntity и TLOG с уважением.

Попробуйте

public TLog GetMapped<TEntity, TLog>(TEntity entity) 
{ 
    if(typeof(TEntity) == typeof(Branch) && typeof(TLog) == typeof(BranchLog)) 
    { 
     B b = new B(); 
     return b.Get(entity); 
    } 
    return null; 
} 

Но вы можете также переопределить метод GetMapped в производного типа B для того, чтобы получить экземпляр этого класса, где вы можете позвонить b.Get(entity)

Однако, если есть больше то это один тип (и обычно это относится к дженерикам, почему еще вы должны их использовать ?!), это не очень хороший вариант.

+2

Вы только что скопировали/вставляли код из моего ответа с небольшим количеством комментариев до/после? – MarcinJuraszek

-1

Самое смешное, что делает GetMapped <> родовое так, клиент из Mapper класса уже точно знать, какой отображение должно быть там. Независимо от конкретной реализации, я бы переосмыслил то, что вы пытаетесь сделать здесь. Не могли бы вы уточнить, что будет использовать метод GetMapped <>?

EDIT: в попытке опубликовать альтернативное решение, почему бы вам не просто поставить конкретные примеры ваших подклассов в реальный словарь внутри Mapper класса:

public class Mapper 
{ 
    private readonly Dictionary<Type, object> _instances; 

    public Mapper() 
    { 
     _instances = new Dictionary<Type, object>() 
     { 
      { typeof(Branch), new B() }, 
      ... 
     }; 
    } 

    public TLog GetMapped<TEntity, TLog>(TEntity entity) 
    { 
     A<TEntity, TLog> a = (A<TEntity, TLog>) _instances[typeof(TEntity)]; 

     return a.Get(entity); 
    } 
} 
0

Проблема здесь является что любое число классов, кроме B, также может распространять общий экземпляр A<Branch,BranchLog>, поэтому он просит компилятор сказать «если B является единственным подклассом A<Branch,BranchLog>, дайте мне экземпляр B»?Без каких-либо других обозначений, связывающих B конкретно с определенным общим методом в классе Mapper, компилятор не может решить это самостоятельно. Если вы попытаетесь сделать это «автомагический» без дженерик или какой-либо другой явной ссылки на Branch,BranchLog ->B то вы, по сути, задающей компилятор для автоматизации эквивалента:

assembly.GetTypes().Where(t => typeof(B).BaseType == t).FirstOrDefault() 

Это на самом деле не специфично для дженерик. Мы можем переписать эту проблему без дженериков, заменив экземпляр A<Branch,BranchLog> простым классом A1.

class A {} 

class A1 : A {} 

class B : A1 {} 

class Mapper { 
    void Fun(A1) { 
     // How to get B b = new B() since I know A1 
     // There is no automatic way at compile time, using the type system 
    } 
} 

Потому что ничто не мешает мне также объявить:

class C : A1 {} 
class D : A1 {} 

Теперь B, C и D все экземпляры A1.

Я думаю, вам нужно переосмыслить свой классный дизайн.

  1. Если вы не можете изменить класс Mapper, рассмотрите какой-либо общий заводский класс или метод. Вы должны иметь возможность написать фабричный метод с 3-мя параметрами общего типа, которые будут сопоставлять конкретный подкласс (B) с конкретными аргументами общих аргументов, используемыми для A<TEntity,TLog>.

    public V GetMapped<T,U,V>(U u) { return new T().Get(U) as V; }

  2. Resort, другие механизмы, чтобы решить карты, которые класс в «подписи», как указано в других ответах (RTTI/есть/а). Но мне кажется, что если вы уже используете дженерики, вы должны придерживаться общего решения.

Смежные вопросы