Можно ли передать подкласс суперкласса в <T>? - PullRequest
3 голосов
/ 09 марта 2012

У меня есть класс с именем GenericDao

internal class GenericDao<T> : IGenericDao<T> {
}

Два класса объектов:

public class Empresa {
}

public class Assessoria : Empresa {
}

И у меня есть один EmpresaDao:

public class EmpresaDao {

    private GenericDao<Empresa> parent { get; set; }

    public EmpresaDao() {
        this.parent = new GenericDao<Empresa>();
    }
}

Как создать экземпляр GenericDao с помощью подкласса Assessoria? Я делаю что-то вроде этого, но не работаю:

public class EmpresaDao {

    private GenericDao<Empresa> parent { get; set; }

    public EmpresaDao(Type type) {
        if (type == typeof(Assessoria)) {
            this.parent = new GenericDao<Assessoria>();
        } else {
            this.parent = new GenericDao<Empresa>();
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 09 марта 2012

Короче говоря, вы не можете, на самом деле. Однако вы можете немного обмануть, если используете базовый интерфейс, который не является универсальным, или вы используете C # 4 и используете базовый интерфейс, который является универсальным, но с ковариантным или контравариантным (в зависимости от необходимости) параметром типа. Для первого случая:

interface IGenericDaoBase {
}

interface IGenericDao<T> : IGenericDaoBase {
}

public class EmpresaDao {
    private IGenericDaoBase parent { get; set; }
    public EmpresaDao(Type type) {
        // same as before
    }
}

По общему признанию, возможно, было бы лучше переосмыслить ваш дизайн. Возможно, EmpresaDao может сам принимать общий параметр, который будет использоваться следующим образом:

public class EmpresaDao<T> where T : Empresa {
    private GenericDao<T> parent { get; set; }
    public EmpresaDao() {
        this.parent = new GenericDao<T>();
    }
}

РЕДАКТИРОВАТЬ: На самом деле, чем больше я думаю об этом, тем больше я верю, что это последнее решение является путь. Параметр типа в конструкторе выполняет ту же роль, что и параметр типа в сигнатуре класса. Таким образом, вам не нужно будет сильно изменять вызывающий код, за исключением передачи общего параметра вместо объекта Type.

1 голос
/ 10 марта 2012

Хорошо, что ваша попытка не сработала, вы бы ввели ошибку, если она сработала.

Предположим, у меня есть переменные a, b типа EmpresaDao.a инициализируется с Empresa родителем, а b инициализируется с Assessoria родителем.Поскольку a и b относятся к одному и тому же типу, должна быть возможность использовать одно вместо другого везде.Предположим, что Assessoria, но не Empresa, имеет метод assess().Но вы ожидаете, что b.parent будет Assessoria, поэтому вы хотите позвонить b.parent.assess(), но вы не можете позвонить a.parent.assess() Это означает, что a и b не должны быть одного типа.

Решение зависит от того, будете ли вы когда-либо вызывать .parent.assess():

a) Если вы никогда не будете вызывать .parent.assess() в классе EmpresaDao, пусть тип времени компиляции родителя всегда будет Empresa,Вот решение:

public class EmpresaDao
{
    private Empresa parent {get; set; }
    public EmpresaDao(Func<Empresa> parentConstructor)
    {
        this.parent = parentConstructor();    
    }
}    
static main()
{
    var withEmpresaParent = new EmpresaDao(() => new Empresa());
    var withAssessoriaParent = new EmpresaDao(() => new Assessoria());
    ..
}

b) Иногда вы будете звонить .parent.assess() в классе EmpresaDao.Затем вы должны сделать EmpresaDao универсальным, как сказал @siride:

public class EmpresaDao<T> where T : Empresa
{
    private T parent {get; set;}
}

Однако все равно вам придется проверять время выполнения для родителя перед вызовом .parent.assess() Что означает, чточто-то не так в вашем дизайне.Но недостаточно информации, чтобы решить, что.Может быть, .assess() метод должен быть закрытым и не вызываться извне (то есть Assessoria должен быть декоратором для Empresa: подкласс, но с тем же интерфейсом) Может быть "Empresa удерживает EmpresaDao" и "Assessoria проведение EmpresaDao "должно быть двух разных классов.(возможно, реализуя тот же интерфейс)

Редактировать: Теперь я понимаю, что в своем решении я по ошибке сделал тип родительского Empresa или Assessoria вместо GenericDao или GenericDao.Я верю, что мое главное все еще в силе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...