Pergunta

Dado o seguinte árvore de herança, o que seria a melhor maneira de implementá-lo de uma forma que funciona?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

Esta falha com:. 'Bar.Bar()' is inaccessible due to its protection level

Eu não quero o construtor sendo público, apenas as classes que herdam Foo deve ser capaz de criar Bars. Bar é um Foo especializada, e qualquer tipo de Foo deve ser capaz de criar um. público interno é uma 'opção' aqui, como a maioria das extensões predefinidas para Foo será interna para a DLL, mas eu considero isso uma resposta superficial, já que qualquer um que vem mais tarde que quer criar seu próprio tipo de Foo ou Baz (o que é provável de acontecer) vai ser preso com uma implementação CreateBar() padrão, que pode ou não pode satisfazer as suas necessidades.

Talvez haja uma maneira de refatoração isso para fazê-lo funcionar bem? Eu estou batendo a cabeça na parede tentando projetar isso para que ele vai trabalhar embora.

Editar (Mais info):

Um pouco mais concreto: Foo está implementando IEnumerable e longa história curta, Bar está fornecendo a mesma interface, mas a um subconjunto limitado de que objeto enumerável. Todos os Foo do deve ser capaz de criar subconjuntos de si mesmos (ie. Bar) e devolvê-lo. Mas eu não quero ter todos os que já quer implementar um Foo ter que se preocupar com isso, porque Bar fará o proxy e preocupação sobre como limitar a gama, etc.

Foi útil?

Solução

Ok, nova resposta:

  1. Split Bar em uma interface e uma classe concreta.
  2. Expresse o método abstrato pública em termos de IBar.
  3. Faça Bar uma classe aninhada privado em Foo, implementando IBar. Dê-lhe um construtor interno, que você pode chamar de Foo.
  4. Escrever um método protegido em Foo que cria uma instância de Bar de si mesmo. Classes decorrentes Foo pode usar isso para implementar o método abstrato que apenas proxy é bom o suficiente, e as classes com necessidades mais complicado pode simplesmente implementar IBar diretamente. Você pode até mesmo alterar o método abstrato para um virtual, e criar uma nova barra de "presente" por padrão.

EDIT: Uma variante deste seria fazer Bar a protegido classe aninhada dentro de Foo, com um construtor público. Dessa forma, qualquer derivado classe seria capaz de instanciar lo por si mesmos, mas nenhuma classe não relacionada seria capaz de "ver" em tudo. Você ainda precisa separar a interface da implementação (de modo que a interface pode ser público), mas eu acho que é uma coisa boa de qualquer maneira.

Outras dicas

Seria possível para você fazer Baz um tipo aninhado dentro Bar? Essa é a única maneira que você vai dar-lhe mais acesso ao Bar do que de outra forma teria. Basta ter a mesma classe pai só dá-lhe acesso a membros protegidos de Foo, e não Foo não têm acesso especial ao Bar. Eu suspeito que há outras formas tortuosas de fazer isso com tipos aninhados, mas realmente vai ser bastante desagradável para os engenheiros de manutenção.

É bem um design estranho, porém, para forçar uma classe derivada para criar uma instância de uma classe diferente derivado da mesma classe base. É que realmente o que você precisa? Talvez se você colocar isso em termos mais concretos que seria mais fácil de vir acima com projetos alternativos.

Você pode acessar o construtor de Bar através de um tipo aninhado dentro Foo:

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}

Esqueça, por um momento, que Bar deriva do Foo. Se você fizer isso, parece que o problema é "Como posso fazer isso de modo que cada subclasse de Foo pode criar um bar, mesmo que a subclasse não tem acesso ao construtor do Bar?"

Isso é um problema muito fácil de resolver:

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

Se você quiser garantia que as subclasses de Foo não têm acesso ao construtor de Bar, colocá-los em um conjunto diferente.

Agora, para derivar Bar de Foo, é uma mudança simples:

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

"Eu não quero que a opinião pública construtor." Check.

"Somente classes que herdam Foo deve ser capaz de criar barras." Check.

"Qualquer tipo de Foo deve ser capaz de criar um." Check,

"Qualquer um que vem mais tarde que quer criar seu próprio tipo de Foo ou Baz (que é provável de acontecer) será preso com um CreateBar padrão de execução (), que pode ou não atender às suas necessidades." Isso é muito altamente dependente que tem que acontecer no método Foo.CreateBar (), eu acho.

C # não fornece um equivalente direto do C ++ amigo palavra-chave. Parece que seu projeto está exigindo esse tipo de construção.

Em C ++ você poderia indicar que uma classe específica tem acesso aos membros privados / protegidas de outra classe usando "amigo". Nota:. Isso não é o mesmo que C # interno, modificador que dá acesso a todas as classes dentro do mesmo assembly

Olhando para a sua concepção, parece que você está tentando fazer algo ao longo das linhas que exigiriam o C ++ amigo estilo. Jon Skeet é certo, o design usual em C # para compensar isso é usar uma classe aninhada.

Este fórum pós explica mais e mostra alguns exemplos de como fazer isso.

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