Pergunta

Quais são as melhores práticas sobre a utilização e estrutura das classes internas em C #.

Por exemplo, se eu tenho uma classe muito grande de base e duas grandes classes internas devo dividi-los em (classe parcial) codefiles separadas ou deixá-los como um grande CodeFile pesado?

Também é má prática para ter uma classe abstrata, com um público herdado classe interna?

Foi útil?

Solução

Tipicamente I reservar interiores-classes por um de dois propósitos:

  1. classes públicas que derivam da sua classe pai no qual a classe pai é uma implementação base abstrata com um ou mais abstratos métodos e cada subclasse é uma implementação que serve uma implementação específica. depois de ler Concepção e Diretrizes Quadro I ver que este é marcado como "evitar", no entanto eu usá-lo em cenários semelhantes a enums - althogh que, provavelmente, está dando uma má impressão assim

  2. As classes internas são privados e são unidades de lógica de negócios ou de outra forma intimamente ligado à sua classe pai de uma maneira em que são fundamentalmente quebrado quando consumidos ou utilizados por qualquer outra classe.

Para todos os outros casos que eu tento mantê-los na mesma o mesmo nível de acessibilidade como seu pai consumidor / lógica namespace e -. Muitas vezes com nomes que são um pouco menos amigável do que a classe "principal"

Em grandes projetos você ficaria surpreso quantas vezes você pode encontrar-se inicialmente a construção de um componente fortemente acoplado só porque é primeiro ou o objetivo principal faz parecer lógico - no entanto, a menos que você tem um muito bom ou técnico razão para bloqueá-lo para baixo e escondê-lo de vista, então há pouco dano em expor a classe para que outros componentes podem consumi-lo.

Editar Tenha em mente que mesmo que estamos falando de sub-classes que devem ser componentes mais ou menos bem concebidos e fracamente acoplados. Mesmo que eles são privados e invisíveis para o mundo exterior mantendo um mínimo "área de superfície" entre as classes facilitará enormemente a capacidade de manutenção do seu código para a expansão ou alteração futuro.

Outras dicas

Eu não tenho o livro a mão, mas o Projeto Diretrizes Framework sugere o uso de public classes internas, desde que os clientes não têm de se referir ao nome da classe. private classes internas são muito bem: ninguém vai notar estes

.

Bad: ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();

Bom: listView.Items.Add(...);

Quanto à sua grande classe: é geralmente dividindo algo de valor como esta em classes menores, cada um com um pedaço específico de funcionalidade. É difícil quebrá-lo inicialmente, mas eu prevejo que vai facilitar a sua vida mais tarde ...

Geralmente classes internas deve ser privado e utilizável apenas pela classe que os contém. Se eles classes internas são muito grandes que gostaria de sugerir que deve ser suas próprias classes.

Normalmente, quando você tem uma grande classe interna é porque a classe interna está intimamente ligado a ele está contendo classe e necessidades de acesso aos seus métodos privados.

Eu acho que isso é bastante subjetiva, mas eu provavelmente iria separá-los em arquivos de código separado, fazendo a classe "host" parcial.

Ao fazer assim, você pode obter ainda mais visão geral por edição o arquivo de projeto para tornar o grupo arquivos como aulas de design em Windows Forms. Eu acho que vi um suplemento Visual Studio que faz isso automagicamente para você, mas eu não me lembro onde.

EDIT:
Depois de alguma procura eu encontrei o suplemento do Visual Studio para fazer isso chamados VSCommands

No que diz respeito só a forma de estruturar uma tal besta ...

Você pode usar classes parciais para dividir a classe principal e as classes aninhadas. Quando você faz isso, você está aconselhado a arquivos nome apropriadamente por isso é óbvio o que está acontecendo.

// main class in file Outer.cs
namespace Demo
{
  public partial class Outer
  {
     // Outer class
  }
}

// nested class in file Outer.Nested1.cs
namespace Demo
{
  public partial class Outer
  {
    private class Nested1
    {
      // Nested1 details
    }
  }
}

Da mesma forma, muitas vezes você vê interfaces (explícitas) em seu próprio arquivo. por exemplo. Outer.ISomeInterface.cs em vez do padrão editor de #regioning-los.

A sua estrutura de arquivos projetos, em seguida, começa a parecer

   /Project/Demo/ISomeInterface.cs
   /Project/Demo/Outer.cs
   /Project/Demo/Outer.Nested1.cs
   /Project/Demo/Outer.ISomeInterface.cs

Normalmente, quando estamos fazendo isso é por uma variação do padrão Builder.

Eu, pessoalmente, gostaria de ter uma classe por arquivo, e as classes internas como parte desse arquivo. Acredito classes internas deve normalmente (quase sempre) ser privada, e são um detalhe de implementação da classe. Tê-los em separado confunde arquivo coisas, IMO.

Usando regiões de código para envolver as classes internas e esconder seus detalhes funciona bem para mim, neste caso, e mantém o arquivo de ser difícil trabalhar com. As regiões de código manter a classe interna "escondida", e já que é um detalhe de implementação privada, que é bom para mim.

Eu pessoalmente uso classes internas para encapsular alguns dos conceitos e operações usados ??apenas internamente dentro de uma classe. Desta forma, eu não poluem essa classe api não-pública e manter a api limpo e compacto.

Você pode tirar proveito de classes parciais para mover a definição destas classes internas em um arquivo diferente para melhor orgnanization. VS não faz automaticamente arquivos de classe parcial do grupo para você, exceto para alguns dos itens templatized tais como formulários ASP.NET, WinForm, etc. Você precisará editar o arquivo de projeto e fazer algumas mudanças lá. Você pode olhar para um dos existentes agrupamento lá para ver como ele é feito. Eu acredito que existem algumas macros que permitem arquivos de classe de grupo parcial para você no explorador solução.

Em minha opinião as classes internas, se necessário em tudo, deve ser mantido pequeno e usado internamente apenas por essa classe. Se você usar Relfector no framework .NET que você vai vê-los muito utilizada apenas para essa finalidade.

Se suas classes internas estão ficando grandes demais Eu definitivamente retirá-los em classes separadas / codefiles de alguma forma, se apenas para manutenção. Eu tenho que suportar algum código existente onde alguém achou que era uma ótima idéia para usar classes internas dentro de classes internas. Isso resultou em uma hierarquia de classe interna correndo 4 - 5 níveis de profundidade. Escusado será dizer que o código é impenetrável e leva uma eternidade para entender o que você está olhando.

Aqui ver um exemplo prático de uma classe aninhada que poderia lhe dar uma idéia de seu uso (adicionado algum teste de unidade)

namespace CoreLib.Helpers
{
    using System;
    using System.Security.Cryptography;

    public static class Rnd
    {
        private static readonly Random _random = new Random();

        public static Random Generator { get { return _random; } }

        static Rnd()
        {
        }

        public static class Crypto
        {
            private static readonly RandomNumberGenerator _highRandom = RandomNumberGenerator.Create();

            public static RandomNumberGenerator Generator { get { return _highRandom; } }

            static Crypto()
            {
            }

        }

        public static UInt32 Next(this RandomNumberGenerator value)
        {
            var bytes = new byte[4];
            value.GetBytes(bytes);

            return BitConverter.ToUInt32(bytes, 0);
        }
    }
}

[TestMethod]
public void Rnd_OnGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Generator;
    var rdn2 = Rnd.Generator;
    var list = new List<Int32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                lock (list)
                {
                    list.Add(Rnd.Generator.Next(Int32.MinValue, Int32.MaxValue));
                }
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}

[TestMethod]
public void Rnd_OnCryptoGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Crypto.Generator;
    var rdn2 = Rnd.Crypto.Generator;
    var list = new ConcurrentQueue<UInt32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                    list.Enqueue(Rnd.Crypto.Generator.Next());
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top