Pergunta

Eu tenho um Bar classe com um campo particular que contém o tipo de referência Foo. Gostaria de expor Foo em uma propriedade pública, mas eu não quero que os consumidores da propriedade a ser capaz de Foo alter ... Deve, contudo, ser alterável internamente pelo Bar, ou seja, eu não posso fazer o readonly campo.

Então, o que eu gostaria é:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

... que é, naturalmente, não é válido. Eu poderia simplesmente retornar um clone de Foo (assumming que é IClonable), mas isso não é óbvio para o consumidor. Devo mudar o nome da propriedade para FooCopy ?? Caso seja um método GetCopyOfFoo em vez disso? O que você considera melhor prática? Obrigado!

Foi útil?

Solução

Parece que você está depois o equivalente a "const" do C ++. Isso não existe em C #. Não há nenhuma maneira de indicar que os consumidores não podem modificar as propriedades de um objeto, mas algo mais lata (assumindo os membros mutantes são públicos, é claro).

Você poderia retornar um clone do Foo como sugerido, ou possivelmente um Ver para o Foo, como ReadOnlyCollection faz para coleções. Claro, se você poderia fazer Foo um tipo imutável, que tornaria a vida mais simples ...

Note-se que há uma grande diferença entre fazer o campo somente leitura e fazendo com que o próprio objeto imutável.

Atualmente, o próprio tipo poderia mudar as coisas em ambos os sentidos. Ele poderia fazer:

_Foo = new Foo(...);

ou

_Foo.SomeProperty = newValue;

Se ele só precisa ser capaz de fazer o segundo, o campo pode ser somente leitura, mas você ainda tem o problema das pessoas que buscam a propriedade de ser capaz de transformar o objeto. Se ele só precisa fazer o primeiro, e realmente Foo é ou já imutável ou poderiam ser feitas imutável, você pode apenas fornecer uma propriedade que só tem o "getter" e você vai ficar bem.

de muito importante que você entenda a diferença entre a alteração do valor do campo (para torná-lo se referir a uma instância diferente) e alterar o conteúdo do objeto que o campo se refere.

Outras dicas

Infelizmente, não há nenhuma maneira fácil de contornar isso em C # no momento. Você poderia extrair a "ler apenas parte" do Foo em uma interface e deixe o retorno de sua propriedade que em vez de Foo.

Fazer uma cópia, um ReadOnlyCollection, ou ter Foo ser imutáveis ??são geralmente os três melhores rotas, como você já especulado.

Sou a favor de métodos às vezes em vez de propriedades sempre que eu estou fazendo nada mais significativo do que simplesmente devolver o campo subjacente, dependendo de quanto trabalho está envolvido. Métodos implica que algo está acontecendo e levantar mais de uma bandeira para os consumidores quando eles estão usando sua API.

"Clonagem" do Foo objetos que você receber e dar de volta para fora é uma prática normal chamado cópia defensiva . A menos que haja algum efeito colateral invisível à clonagem que será visível para o usuário, não há absolutamente nenhuma razão para não fazer isso. Muitas vezes, é a única maneira de proteger dados privados internos das suas classes, especialmente em C # ou Java, onde a idéia C ++ de const não está disponível. (IE, isso deve ser feito de modo a criar corretamente objetos verdadeiramente imutáveis ??nestas duas línguas.)

Só para esclarecer, possíveis efeitos colaterais seriam coisas como o seu utilizador (razoavelmente) que esperam que o objeto original ser devolvido, ou algum recurso que está sendo realizada por Foo que não vai ser clonado corretamente. (Nesse caso, o que ele está fazendo implementação IClonable?!)

Se você não quer que ninguém mexer com o seu estado ... não o exponha! Como já foi dito, se algo precisa ver o seu estado interno, fornecer uma representação imutável dele. Como alternativa, obter clientes para contar você para fazer alguma coisa (no Google por "dizer não pergunte"), em vez de fazê-lo eles mesmos.

Para esclarecer o comentário de Jon Skeet você pode fazer um ponto de vista, que é uma classe wrapper imutável para o Foo mutável. Aqui está um exemplo:

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}

Você pode realmente reproduzir o comportamento de const C ++ em C # -. Você só tem que fazê-lo manualmente

Whatever Foo é, a única maneira que o chamador pode modificar seu estado é chamando métodos nele ou definição de propriedades.

Por exemplo, Foo é do tipo FooClass:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

Assim, no seu caso, você está feliz por eles para obter a propriedade Message, mas não defini-lo, e você não está definitivamente feliz sobre eles chamar esse método.

Assim, definir uma interface descrevendo o que eles estão autorizados a fazer:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

Nós deixamos o método mutação e apenas à esquerda na getter na propriedade.

Em seguida, adicione essa interface para a lista de base do FooClass.

Agora, em sua classe com a propriedade Foo, você tem um campo:

private FooClass _foo;

E um getter propriedade:

public IFooConst Foo
{
    get { return _foo; }
}

Esta reproduz basicamente à mão precisamente o que o C ++ const palavra-chave faria automaticamente. Em termos pseudo-C ++, uma referência de tipo const Foo & é como um tipo gerado automaticamente que inclui apenas aqueles membros de Foo que foram marcados como membros const. Traduzindo isso em alguma versão teórica futuro do C #, você declarar FooClass assim:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

Realmente tudo o que eu tenho feito é fundiu as informações em IFooConst volta para FooClass, marcando o único membro seguro com uma nova palavra-chave const. Então, de certa forma, adicionando uma palavra-chave const não iria acrescentar muito à linguagem, além de uma abordagem formal para esse padrão.

Em seguida, se você tivesse uma referência const a um objeto FooClass:

const FooClass f = GetMeAFooClass();

Você só seria capaz de chamar os membros const em f.

Note que, se a definição FooClass é público, o chamador poderia lançar um IFooConst em um FooClass. Mas eles podem fazer isso em C ++ também -. É chamado de "jogar fora const" e envolve um operador especial chamado const_cast<T>(const T &)

Há também a questão de interfaces não ser muito fácil de evoluir entre as versões do seu produto. Se um terceiro pode implementar uma interface que define (que eles são livres para fazer se eles podem vê-lo), então você não pode adicionar novos métodos para que em futuras versões sem a necessidade de outros para recompilar seu código. Mas isso é apenas um problema se você estiver escrevendo uma biblioteca extensível para os outros a construir. Talvez um recurso interno const resolveria este problema.

Eu estava pensando sobre as coisas de segurança semelhantes. Há provavelmente uma maneira. Bastante clara, mas não curto. A idéia geral é bastante simples. No entanto, eu sempre achei algumas maneiras em torno por isso nunca testei. Mas você pode verificá-lo -. Talvez ele vai trabalhar para você

Este é um código pseudo, mas espero idéia por trás dele é clara

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

Assim, na classe que cria esta propriedade você tem acesso ao método SetMe, mas ainda é privada de modo exceto para criador Foo parece unmutable.

Ainda para qualquer coisa maior do que algumas propriedades isso provavelmente tornou-se logo de super bagunça - é por isso que eu sempre preferi outras formas de encapsulamento. No entanto, se ele é super importante para você não permitir que o cliente mudar Foo, que esta é uma alternativa.

No entanto, como eu disse, isso é apenas teoria.

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