2015-11-12 4 views
1

Есть ли способ сделать то, что я хочу здесь?Использовать тип конкретной реализации в абстрактном базовом классе

Базовый класс определяется следующим образом:

public abstract class BaseClass<TExists> 
    where TExists : BaseExists 
{ 
    // needs to be overridden by child 
    protected abstract bool Exists(TExists existsData, out /*typeof(this)*/ existingElement); // <- how to have the concrete type here? 

    // static method to be invoked without any need of an instance 
    public static bool Exists(TExists existsData, out /*typeof(this)*/ existingElement) 
    { 
     var temp; // <-- how to set the type here? 

     // create a concrete instance 
     var instance = Activator.CreateInstance(???); 

     // call the concrete implementation 
     if(instance.Exists(existsData, out temp)) 
     { 
      return true; 
     } 

     return false; 
    } 
} 

И здесь у нас есть некоторые конкретные реализации:

public class ChildClass : BaseClass<ChildClassExists> 
{ 
    protected override bool Exists(ChildClassExists exists, out ChildClass existingElement) 
    { 
     // do child-related things here 
    } 
} 

В конце я хочу использовать его как

ChildClass existing;  
if(ChildClass.Exists(new ChildClassExists(), out existing)){ 
    // do things here with the existing element of type 'ChildClass' 
} 

потому что мне здесь не нужен экземпляр (это скрыто внутри реализации базового класса Exists).

Update # 1

Как реализуется как в промежуточные кадры первого ответа теперь у меня есть:

public static bool Exists<T>(TExists existsModel, out T existingEntityModel) 
    where T : BaseClass<TExists> 
{ 
    var instance = Activator.CreateInstance<T>(); 
    return instance.ExistsInternal(existsModel, out existingEntityModel); 
} 

protected abstract bool ExistsInternal<T>(TExists createModel, out T existingEntityModel) 
    where T : BaseClass<TExists>; 

Но это вызовет ошибку в конкретной реализации метода ExistsInternal

Невозможно преобразовать тип источника 'ChildClass' в целевой тип 'T'

в переопределение

protected override bool ExistsInternal<T>(ChildClassExists existsData, out T existingElement) 
{ 
    existingElement = new ChildClass(); // <-- here the error is thrown 
    return true; 
} 
+1

Вам нужно будет добавить еще один общий параметр ('TConcrete'). Но в целом проблема связана с плохим дизайном. Кроме того, вместо 'Activator.CreateInstance()' вы сможете сделать 'новый TConcrete()' (предполагая, что вы сможете добавить ограничение 'new()') – haim770

+0

Вы пробовали 'out TExists existingElement 'и' TExists temp; '? –

+0

@FabioLuz: 'TExists' является базовым типом объекта, содержащего данные для проверки наличия, а не типа объекта, который должен быть возвращен. – KingKerosin

ответ

2

Просто добавьте новый общий параметр в методе Exists. Этот тип будет выведен компилятором, так что нет никакого реального влияния на удобстве использования:

public abstract class BaseClass<TExists> where TExists : BaseExists 
{ 
    // needs to be overridden by child 
    protected abstract bool InternalExistsCheck<T>(TExists existsData, out T existingElement) where T : BaseClass<TExists>, new(); 

    // static method to be invoked without any need of an instance 
    public static bool Exists<T>(TExists existsData, out T existingElement) where T : BaseClass<TExists>, new() 
    { 
     T temp; // <-- how to set the type here? 
     existingElement = null; 

     // create a concrete instance 
     var instance = new T(); 

     // call the concrete implementation 
     if (instance.InternalExistsCheck(existsData, out temp)) 
     { 
      existingElement = temp; 
      return true; 
     } 

     return false; 
    } 
} 

Обратите внимание, что если вы не измените защищенный Exists метод, вы получите ошибку во время компиляции Неоднозначного вызова (VS 2013).

Теперь его прекрасно сделать:

public class ChildClass : BaseClass<ChildClassExists> 
{ 
    protected override bool InternalExistsCheck<T>(ChildClassExists exists, out T existingElement) 
    { 
     .... 
    } 
} 

и

ChildClass existing; 

if (ChildClass.Exists(new ChildClassExists(), out existing)) 
{ 
    // do things here with the existing element of type 'ChildClass' 
} 

UPDATE

адресация Ваше беспокойство о том, не будучи в состоянии назначить ChildInstance для existing в перекрываться ChildClass.InternalExistsCheck(,), да вы можете просто сделать:

existing = new T(); 

Если T является ChildClass (выведен компилятором), то вы будете создавать ChildClass экземпляр. Имейте в виду, что вы получаете BaseClass<ChildExists> типизированную ссылку, а не ChildClass.

Если вы абсолютно нужен ChildClass набранный ссылку, то есть обходной путь (если вам нужно сделать это, его, вероятно, потому, что дженерики не является правильным инструментом для Вас):

var childClassTypedReference = (object)existing as ChildClass. 

ли понимать, что в целом решение не так безопасно, как вам хотелось бы; вы должны считать возможность existing не являющейся ChildClass типизированной ссылкой (и поэтому childClassTypedReference является null). Нет ничего предохраняющего existing от любой тип удлинения BaseClass<ChildExists>.

У меня нет целой картинки вашего кода, но я действительно думаю, что вы злоупотребляете дженериками здесь. Я думаю, что не общий подход с зависимостью интерфейса был бы более чистым подходом.

+0

'default (T)' всегда будет возвращать 'null' в этом случае, и вы получите NRE в следующей строке – haim770

+0

@ haim7770: да, его' new T() '. Не думал правильно: p Спасибо – InBetween

+0

У меня все еще есть проблемы с параметром 'out' типа' T', который не может быть установлен в 'ChildClass' переопределении 'InternalExistsCheck'. Не видишь, что мне не хватает. – KingKerosin

0

Вдохновленный комментариями/ответами, я придумал следующее решение, которое идеально соответствует моим потребностям.

1 Добавлен новый (не родовой) базовый класс BaseClass

-Пусть общие базы наследует от необщего

public abstract class BaseClass<TExists> : BaseClass 
{ 
} 

3 Настройки abstract и static методов в BaseClass<TExists>

// abstract to be overriden by child-classes 
protected abstract BaseClass ExistsInternal(TExists existsModel); 

public static bool Exists<T>(TExists existsModel, out T existing) where T : BaseClass<TExists> 
{ 
    // create a new instance of the concrete child 
    var instance = (T) Activator.CreateInstance<T>; 

    // set the out parameter (can be null) 
    existing = instance.ExistsInternal(existsModel) as T; 

    // return if already existing 
    return existing!= null; 
} 

4 Внедрение переопределения в дочерних классах

protected override BaseClass ExistsInternal(ChildClassExists existsModel) 
{ 
    // check for existing 
    var existing = ...; // do child-class dependend stuff 

    // return concrete instance or null 
    return existing != null ? new ChildClass(existing) : null; 
} 

5 Вызов его (с типами, выведенных из эксплуатации через ChildClass)

ChildClass childClass; 
if (ChildClass.Exists(childClassExists, out childClass)) 
{ 
    // do things with existing childClass 
} 

6 сказать спасибо Inbetween и haim7770

@InBetween, @ haim7770 -> Спасибо! ;)

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