Pergunta

Imagina uma classe base com muitos construtores e um método virtual

public class Foo
{
   ...
   public Foo() {...}
   public Foo(int i) {...}
   ...
   public virtual void SomethingElse() {...}
   ...
}

e agora eu quero criar uma classe descendente que substitui o método virtual:

public class Bar : Foo 
{
   public override void SomethingElse() {...}
}

E outro descendente que faz mais algumas coisas:

public class Bah : Bar
{
   public void DoMoreStuff() {...}
}

Será que eu realmente tem que copiar todos os construtores de Foo em Bar e Bah? E então, se eu mudar a assinatura de construtor de Foo, eu tenho que atualizá-lo no Bar e Bah?

Não há nenhuma maneira de construtores herdar? Não há nenhuma maneira de incentivar a reutilização de código?

Foi útil?

Solução

Sim, você terá que implementar os construtores que fazem sentido para cada derivação e, em seguida, usar a palavra-chave base para dirigir esse construtor para a classe base apropriado ou a palavra-chave this para dirigir um construtor para outro construtor na mesma classe.

Se o compilador fez suposições sobre construtores herdando, não seríamos capazes de determinar corretamente como nossos objetos foram instanciado. Na maior parte, você deve considerar porque você tem tantos construtores e considerar reduzindo-os a apenas um ou dois na classe base. As classes derivadas podem mascarar alguns deles usando valores constantes como null e só expor as necessárias através de seus construtores.

Atualização

Em C # 4, você pode especificar valores de parâmetro padrão e usar parâmetros nomeados para fazer um único apoio construtor múltiplas configurações de argumento ao invés de ter um construtor por configuração.

Outras dicas

387 construtores ?? Esse é o seu principal problema. Como sobre isto em vez disso?

public Foo(params int[] list) {...}

Sim, você tem que copiar todos os 387 construtores. Você pode fazer alguma reutilização, redirecionando-os:

  public Bar(int i): base(i) {}
  public Bar(int i, int j) : base(i, j) {}

mas isso é o melhor que você pode fazer.

É uma pena que é meio forçado a dizer ao compilador o óbvio:

Subclass(): base() {}
Subclass(int x): base(x) {}
Subclass(int x,y): base(x,y) {}

Eu só preciso fazer 3 construtores em 12 subclasses, por isso não é grande coisa, mas eu não gosto muito de repetir que em cada subclasse, depois de ter sido usado para não ter que escrever isso por tanto tempo. Eu tenho certeza que há uma razão válida para isso, mas eu não acho que eu já encontrou um problema que requer este tipo de restrição.

Não se esqueça que você também pode construtores redirecionamento para outros construtores no mesmo nível de herança:

public Bar(int i, int j) : this(i) { ... }
                            ^^^^^

Uma solução simples pode ser a utilização de uma estrutura ou classe de dados simples que contém os parâmetros como propriedades; Dessa forma, você pode ter todos os valores e comportamentos estabelecidos antes do tempo padrão, passando a "classe parâmetro" como o parâmetro construtor único:

public class FooParams
{
    public int Size...
    protected myCustomStruct _ReasonForLife ...
}
public class Foo
{
    private FooParams _myParams;
    public Foo(FooParams myParams)
    {
          _myParams = myParams;
    }
}

Isso evita a confusão de vários construtores (às vezes) e dá tipagem forte, valores padrão e outros benefícios não previstos por uma matriz de parâmetro. Também torna mais fácil para levar adiante já que qualquer coisa que herda de Foo ainda pode chegar, ou mesmo adicionar, FooParams conforme necessário. Você ainda precisa copiar o construtor, mas você sempre (na maioria das vezes), só (como regra geral) sempre (pelo menos, por enquanto) necessidade de um construtor.

public class Bar : Foo
{
    public Bar(FooParams myParams) : base(myParams) {}
}

Eu realmente gosto do sobrecarregado Initailize () e padrão de classe de fábrica se aproxima melhor, mas às vezes você só precisa ter um construtor inteligente. Apenas um pensamento.

Como Foo é uma classe que você não pode criar métodos Initialise() sobrecarregados virtuais? Em seguida, eles estariam disponíveis para sub-classes e ainda extensível?

public class Foo
{
   ...
   public Foo() {...}

   public virtual void Initialise(int i) {...}
   public virtual void Initialise(int i, int i) {...}
   public virtual void Initialise(int i, int i, int i) {...}
   ... 
   public virtual void Initialise(int i, int i, ..., int i) {...}

   ...

   public virtual void SomethingElse() {...}
   ...
}

Isto não deve ter um custo maior desempenho a menos que você tem um monte de valores de propriedade padrão e você batê-lo muito.

public class BaseClass
{
    public BaseClass(params int[] parameters)
    {

    }   
}

public class ChildClass : BaseClass
{
    public ChildClass(params int[] parameters)
        : base(parameters)
    {

    }
}

Pessoalmente, eu acho que isso é um erro em Microsofts parte, eles deveriam ter permitido que o programador visibilidade substituição de Construtores, métodos e propriedades em classes de base, e, em seguida, fazer com que construtores são sempre herdadas.

Desta forma, nós simplesmente substituir (com menor visibilidade -. Ie Privada) os construtores nós não queremos, em vez de ter que adicionar todos os construtores que queremos. Delphi faz isso dessa maneira, e eu sinto falta.

Tome por exemplo, se você quiser substituir a classe System.IO.StreamWriter, você precisa adicionar todos os 7 construtores para sua nova classe, e se você gosta de comentar você precisa comentar cada um com o XML cabeçalho. Para piorar as coisas, a visão metadados dosnt colocar os comentários XML como XML comentários apropriados, por isso temos que ir linha por linha e copiar e colá-los. Qual foi a Microsoft pensando aqui?

eu realmente escrevi um pequeno utilitário onde você pode colar no código de metadados e que irá convertê-lo para comentários XML usando a visibilidade overidden.

Será que eu realmente tem que copiar todos os construtores de Foo em Bar e Bah? E então, se eu mudar a assinatura de construtor em Foo, eu tenho que atualizá-lo em Bar e Bah?

Sim, se você usar construtores para criar instâncias.

Não há nenhuma maneira de construtores herdar?

Não.

Não há nenhuma maneira de incentivar a reutilização de código?

Bem, eu não vou entrar em se herdar construtores seria um bom ou mau coisa e se ele iria incentivar a reutilização de código, uma vez que não os têm e nós não estamos indo para obtê-los. : -)

Mas aqui em 2014, com a corrente C #, você pode obter algo muito como construtores herdado por utilizando um método create genérico em vez. Ele pode ser uma ferramenta útil para ter em seu cinto, mas você não iria alcançá-lo de ânimo leve. Estendi a mão para ele recentemente, quando confrontados com a necessidade de passar algo para o construtor de um tipo base usado em um par de centenas de classes derivadas (até recentemente, a base não precisava de nenhum argumento, e assim o construtor padrão era bom - o derivado aulas não declarar construtores em tudo, e tenho a um automaticamente-fornecido).

Parece que este:

// In Foo:
public T create<T>(int i) where: where T : Foo, new() {
    T obj = new T();
    // Do whatever you would do with `i` in `Foo(i)` here, for instance,
    // if you save it as a data member;  `obj.dataMember = i;`
    return obj;
}

Isso diz que você pode chamar a função create genérico usando um parâmetro de tipo que é qualquer subtipo de Foo que tem um construtor de zero-argumentos.

Então, em vez de fazer Bar b new Bar(42), você faria o seguinte:

var b = Foo.create<Bar>(42);
// or
Bar b = Foo.create<Bar>(42);
// or
var b = Bar.create<Bar>(42); // But you still need the <Bar> bit
// or
Bar b = Bar.create<Bar>(42);

Não que eu mostrei o método create estar em Foo diretamente, mas é claro que poderia estar em uma classe de fábrica de algum tipo, se a informação é a criação pode ser definido por essa classe de fábrica.

Apenas para maior clareza:. O nome create não é importante, que poderia ser makeThingy ou qualquer outra coisa que você gosta

Exemplo completa

using System.IO;
using System;

class Program
{
    static void Main()
    {
        Bar b1 = Foo.create<Bar>(42);
        b1.ShowDataMember("b1");

        Bar b2 = Bar.create<Bar>(43); // Just to show `Foo.create` vs. `Bar.create` doesn't matter
        b2.ShowDataMember("b2");
    }

    class Foo
    {
        public int DataMember { get; private set; }

        public static T create<T>(int i) where T: Foo, new()
        {
            T obj = new T();
            obj.DataMember = i;
            return obj;
        }
    }

    class Bar : Foo
    {
        public void ShowDataMember(string prefix)
        {
            Console.WriteLine(prefix + ".DataMember = " + this.DataMember);
        }
    }
}

O problema não é que Bar e Bah tem que copiar 387 construtores, o problema é que Foo tem 387 construtores. Foo faz claramente muitas coisas - refactor rápido! Além disso, a menos que tenha uma boa razão para ter valores definidos no construtor (que, se você fornecer um construtor sem parâmetros, você provavelmente não), eu recomendo usar propriedade obter / configuração.

Não, você não precisa copiar todos os 387 construtores para Bar e Bah. Bar e Bah pode ter tantos ou tão poucos construtores como você deseja independente de quantas você define na Foo. Por exemplo, você pode optar por ter apenas um construtor Bar que constrói Foo com construtor 212th do Foo.

Sim, nenhum construtor que você alterar nos Foo que Bar ou Bah dependem vai exigir que você modificar Bar e Bah em conformidade.

Não, não há nenhuma maneira em .NET para construtores herdar. Mas você pode conseguir a reutilização de código, chamando o construtor de uma classe base dentro construtor da subclasse ou chamando um método virtual que você definir (como Initialize ()).

Você pode ser capaz de se adaptar uma versão do C ++ construtor virtual idioma . Tanto quanto eu sei, C # não suporta tipos de retorno covariantes. Eu acredito que está na lista de desejos de muitas pessoas.

Muitas construtores é um sinal de um projeto quebrado. Melhor uma classe com alguns construtores ea capacidade de definir propriedades. Se você realmente precisa de controle sobre as propriedades, considere uma fábrica no mesmo namespace e fazer os setters de propriedade interna. Deixe a fábrica de decidir como instanciar a classe e definir suas propriedades. A fábrica pode ter métodos que levam quantos parâmetros necessários para configurar o objeto corretamente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top