Por que não posso herdar classes estáticas?


224

Eu tenho várias classes que realmente não precisam de nenhum estado. Do ponto de vista organizacional, eu gostaria de colocá-los em hierarquia.

Mas parece que não posso declarar herança para classes estáticas.

Algo parecido:

public static class Base
{
}

public static class Inherited : Base
{
}

não funciona.

Por que os designers da linguagem fecharam essa possibilidade?


Respostas:


174

Citação a partir daqui :

Isso é realmente por design. Parece não haver boas razões para herdar uma classe estática. Possui membros estáticos públicos que você sempre pode acessar através do próprio nome da classe. As únicas razões que vi para herdar coisas estáticas foram ruins, como salvar alguns caracteres de digitação.

Pode haver razões para considerar mecanismos para colocar membros estáticos diretamente no escopo (e de fato consideraremos isso após o ciclo do produto Orcas), mas a herança de classe estática não é o caminho a seguir: é o mecanismo errado a ser usado e funciona somente para membros estáticos que residem em uma classe estática.

(Mads Torgersen, linguagem C # PM)

Outras opiniões do channel9

A herança no .NET funciona apenas na base da instância. Os métodos estáticos são definidos no nível do tipo e não no nível da instância. É por isso que a substituição não funciona com métodos / propriedades / eventos estáticos ...

Os métodos estáticos são mantidos apenas uma vez na memória. Não há tabela virtual etc. criada para eles.

Se você chamar um método de instância no .NET, sempre fornecerá a instância atual. Isso está oculto pelo tempo de execução do .NET, mas acontece. Cada método de instância tem como primeiro argumento um ponteiro (referência) para o objeto em que o método é executado. Isso não acontece com métodos estáticos (como eles são definidos no nível do tipo). Como o compilador deve decidir selecionar o método a ser chamado?

(littleguru)

E, como uma ideia valiosa, o littleguru tem uma "solução alternativa" parcial para esse problema: o padrão Singleton .


92
Falta de imaginação do lado de Torgersen, na verdade. Eu tinha uma boa razão para querer fazer isso :)
Thorarin

10
Que tal agora? Você tem uma dll que não é de código aberto / não tem acesso a ele, digamos NUnit. Você deseja estender a classe Assert para ter um método como Throws <> (Ação a) (sim, também existe, mas a certa altura não estava.) Você pode adicionar ao seu projeto um Assert: NUnit.Framework.Assert classe e, em seguida, adicione um método Throws. Problema resolvido. É mais fácil usar o código também ... em vez de precisar criar outro nome de classe e lembrar-se desse nome, basta digitar Assert. e veja os métodos disponíveis.
user420667

4
@KonradMorawski É impossível escrever métodos de extensão para classes estáticas. stackoverflow.com/questions/249222/…
Martin Braun

2
@modiX Claro. Você me entendeu mal aqui. O que eu quis dizer é que a funcionalidade necessária no cenário descrito por user420667 poderia ser fornecida ( no futuro ) por apenas permitir métodos de extensão para classes estáticas. Nenhuma herança estática é necessária. É por isso que o cenário dele não me pareceu uma boa justificativa para a introdução de herança estática. Se o C # deveria ser modificado nesse aspecto, por que fazer uma reformulação importante quando uma menor seria tão boa na prática.
Konrad Morawski

5
@ Aluno Isso não é realmente justo. No .Net tudo é herdado objecte todos devem saber disso. Portanto, as classes estáticas sempre são herdadas object, independentemente de você ser especificado explicitamente ou não.
RenniePet

71

A principal razão pela qual você não pode herdar uma classe estática é que elas são abstratas e seladas (isso também impede que qualquer instância delas seja criada).

Então, é isso:

static class Foo { }

compila para este IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }

17
Você está dizendo que a estática foi implementada como abstrata + selada. Ele quer saber por que isso foi feito. Por que está selado?
22410 Lucas

22
Isso não responde à pergunta de tudo; apenas reafirma o problema que ele estava perguntando.
Igby Largeman

28
Bem, o OP deveria ter perguntado "Por que as classes estáticas estão seladas", e não "Por que não posso herdar das classes estáticas?", Cuja resposta é obviamente "porque elas estão seladas" . Esta resposta recebe meu voto.
Alex Budovski

6
obrigado por compartilhar que as classes estáticas são compiladas automaticamente como classes seladas - eu queria saber como / quando isso aconteceu!
Mark

23
@AlexBudovski: Esta resposta é correta, mas tão inútil quanto dizer a uma pessoa perdida "você está na minha frente" quando ela pergunta "onde estou".
DeepSpace101

24

Pense desta maneira: você acessa membros estáticos através do nome do tipo, assim:

MyStaticType.MyStaticMember();

Para herdar dessa classe, você teria que acessá-la através do novo nome de tipo:

MyNewType.MyStaticMember();

Portanto, o novo item não possui relações com o original quando usado no código. Não haveria como tirar proveito de qualquer relacionamento de herança para coisas como polimorfismo.

Talvez você esteja pensando em apenas estender alguns dos itens da classe original. Nesse caso, não há nada que o impeça de usar apenas um membro do original em um tipo totalmente novo.

Talvez você queira adicionar métodos a um tipo estático existente. Você pode fazer isso já através de métodos de extensão.

Talvez você queira passar uma estática Typepara uma função em tempo de execução e chamar um método nesse tipo, sem saber exatamente o que o método faz. Nesse caso, você pode usar uma interface.

Portanto, no final, você realmente não ganha nada com a herança de classes estáticas.


5
Você não pode adicionar métodos a um tipo estático existente por meio de um método de extensão. Por favor, veja meu comentário na resposta aceita para um exemplo de uso desejado. (Basicamente você quer apenas mudar o nome que você chamado MyNewType como MyStaticType, então MyStaticType: OldProject.MyStaticType)
user420667

2
Pode-se definir relações de herança com tipos estáticos e membros estáticos virtuais de tal maneira que a pessoa SomeClass<T> where T:Foopossa acessar membros Fooe, se houver Tuma classe que Foosubstitua alguns membros estáticos virtuais , a classe genérica usará essas substituições. Provavelmente seria possível definir uma convenção através da qual os idiomas poderiam fazê-lo de maneira compatível com o CLR atual (por exemplo, uma classe com esses membros deve definir uma classe não estática protegida que contenha esses membros da instância, juntamente com um campo estático segurando uma instância desse tipo).
Supercat 29/12

Me deparei com um caso em que acredito que herdar uma classe estática é a coisa certa a se fazer, embora obviamente você possa acessá-las de qualquer maneira. Eu tenho uma classe estática para enviar fluxos de dados brutos para a impressora (para conversar com impressoras de etiquetas industriais). Ele tem várias declarações de API do Windows. Agora me pego escrevendo outra classe para mexer com impressoras que precisam das mesmas chamadas de API. Combinar? Não é bom. Declará-los duas vezes? Feio. Herdar? Bom, mas não permitido.
Loren Pechtel

3

O que você deseja obter usando a hierarquia de classes pode ser alcançado apenas através do namespace. Portanto, idiomas que suportam namespapces (como C #) não terão utilidade em implementar a hierarquia de classes de classes estáticas. Como você não pode instanciar nenhuma das classes, tudo o que você precisa é de uma organização hierárquica das definições de classe que você pode obter através do uso de namespaces


Eu tenho um carregador genérico que recebe uma classe como tipo. Essa classe possui 5 métodos auxiliares e 2 métodos que retornam uma sequência (nome e descrição). Eu posso ter entendido errado (acho que sim), mas a única solução que encontrei foi instanciar a classe no carregador genérico, acessar os métodos ... meh.
ANeves

A alternativa seria um dicionário gigante, eu acho. Além disso, quando você diz "o que deseja alcançar", deve dizer "o que parece estar buscando" ou algo semelhante.
ANeves

3

Você pode usar a composição ... isso permitirá acessar objetos de classe do tipo estático. Mas ainda não é possível implementar interfaces ou classes abstratas


2

Embora você possa acessar membros estáticos "herdados" por meio do nome de classes herdadas, os membros estáticos não são realmente herdados. Isso é em parte porque eles não podem ser virtuais ou abstratos e não podem ser substituídos. No seu exemplo, se você declarou um Base.Method (), o compilador mapeará uma chamada para Inherited.Method () de volta para Base.Method () de qualquer maneira. Você também pode chamar Base.Method () explicitamente. Você pode escrever um pequeno teste e ver o resultado com o Refletor.

Então ... se você não puder herdar membros estáticos e se as classes estáticas puderem conter apenas membros estáticos, que bem herdaria uma classe estática?


1

Hmmm ... seria muito diferente se você tivesse apenas classes não estáticas preenchidas com métodos estáticos ..?


2
Isso é o que me resta. Eu faço assim. E eu não gosto disso.
Utilizador

Também fiz dessa maneira, mas por algum motivo não parece estético quando você sabe que nunca instanciará o objeto. Eu sempre sofro por detalhes como esse, apesar do fato de que não há custo material disso.
Gdbj

Na classe non-static cheia de métodos estáticos que você não pode colocar métodos de extensão :)
Jakub Szułakiewicz

1

Uma solução alternativa que você pode fazer é não usar classes estáticas, mas ocultar o construtor para que os membros estáticos das classes sejam a única coisa acessível fora da classe. O resultado é uma classe "estática" herdável essencialmente:

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

Infelizmente, devido à limitação de idioma "projetada", não podemos fazer:

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}

1

Você pode fazer algo que se parecerá com herança estática.

Aqui está o truque:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

Então você pode fazer isso:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

A Inheritedclasse não é estática em si, mas não permitimos criá-la. Na verdade, ele herdou o construtor estático que constrói Basee todas as propriedades e métodos Basedisponíveis como estáticos. Agora, a única coisa que resta a fazer é invólucros estáticos para cada método e propriedade que você precisa expor ao seu contexto estático.

Existem desvantagens, como a necessidade de criação manual de métodos e newpalavras - chave estáticos do wrapper . Mas essa abordagem ajuda a oferecer suporte a algo realmente semelhante à herança estática.

PS Usamos isso para criar consultas compiladas e, na verdade, isso pode ser substituído pelo ConcurrentDictionary, mas um campo estático de somente leitura com sua segurança de thread era bom o suficiente.


1

Minha resposta: má escolha de design. ;-)

Este é um debate interessante focado no impacto da sintaxe. O núcleo do argumento, na minha opinião, é que uma decisão de design levou a classes estáticas seladas. Um foco na transparência dos nomes da classe estática aparecendo no nível superior em vez de se esconder ('confuso') atrás dos nomes filhos? Pode-se imaginar uma implementação de linguagem que poderia acessar diretamente a base ou a criança, confusa.

Um pseudo exemplo, assumindo que a herança estática foi definida de alguma forma.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

levaria a:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

qual (impactaria) o mesmo armazenamento que

// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

Muito confuso!

Mas espere! Como o compilador lidaria com MyStaticBase e MyStaticChild com a mesma assinatura definida em ambos? Se o filho substitui o exemplo acima, NÃO mudaria o mesmo armazenamento, talvez? Isso leva a ainda mais confusão.

Acredito que haja uma forte justificativa de espaço informacional para herança estática limitada. Mais sobre os limites em breve. Este pseudocódigo mostra o valor:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

Então você obtém:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

O uso se parece com:

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

A pessoa que cria o filho estático não precisa pensar no processo de serialização, desde que entenda as limitações que possam ser colocadas no mecanismo de serialização da plataforma ou do ambiente.

As estáticas (singletons e outras formas de 'globais') surgem frequentemente em torno do armazenamento da configuração. A herança estática permitiria que esse tipo de alocação de responsabilidade seja representado de maneira limpa na sintaxe para corresponder a uma hierarquia de configurações. Embora, como mostrei, haja muito potencial para ambiguidade massiva se conceitos básicos de herança estática forem implementados.

Acredito que a escolha certa do projeto seria permitir a herança estática com limitações específicas:

  1. Nenhuma substituição de nada. O filho não pode substituir os atributos, campos ou métodos base, ... A sobrecarga deve estar ok, desde que haja uma diferença na assinatura que permita ao compilador classificar filho versus base.
  2. Permitir apenas bases estáticas genéricas, você não pode herdar de uma base estática não genérica.

Você ainda pode alterar a mesma loja por meio de uma referência genérica MyStaticBase<ChildPayload>.SomeBaseField. Mas você ficaria desanimado, pois o tipo genérico precisaria ser especificado. Enquanto a referência criança seria mais limpo: MyStaticChild.SomeBaseField.

Não sou um escritor de compilador, portanto, não tenho certeza se estou perdendo algo sobre as dificuldades de implementar essas limitações em um compilador. Dito isso, acredito firmemente que há um espaço informativo necessário para herança estática limitada e a resposta básica é que você não pode por causa de uma escolha de design pobre (ou muito simples).


0

Classes estáticas e membros da classe são usados ​​para criar dados e funções que podem ser acessados ​​sem a criação de uma instância da classe. Os membros da classe estática podem ser usados ​​para separar dados e comportamentos independentes de qualquer identidade de objeto: os dados e as funções não são alterados, independentemente do que acontece com o objeto. Classes estáticas podem ser usadas quando não há dados ou comportamento na classe que dependa da identidade do objeto.

Uma classe pode ser declarada estática, o que indica que ela contém apenas membros estáticos. Não é possível usar a nova palavra-chave para criar instâncias de uma classe estática. As classes estáticas são carregadas automaticamente pelo CLR (Common Language Runtime) do .NET Framework quando o programa ou espaço para nome que contém a classe é carregado.

Use uma classe estática para conter métodos que não estão associados a um objeto específico. Por exemplo, é um requisito comum criar um conjunto de métodos que não atuam nos dados da instância e não estão associados a um objeto específico no seu código. Você pode usar uma classe estática para armazenar esses métodos.

A seguir, estão os principais recursos de uma classe estática:

  1. Eles contêm apenas membros estáticos.

  2. Eles não podem ser instanciados.

  3. Eles estão selados.

  4. Eles não podem conter construtores de instância (Guia de Programação em C #).

Portanto, criar uma classe estática é basicamente o mesmo que criar uma classe que contém apenas membros estáticos e um construtor privado. Um construtor privado impede que a classe seja instanciada.

A vantagem de usar uma classe estática é que o compilador pode verificar para garantir que nenhum membro da instância seja adicionado acidentalmente. O compilador garantirá que instâncias dessa classe não possam ser criadas.

As classes estáticas são seladas e, portanto, não podem ser herdadas. Eles não podem herdar de nenhuma classe, exceto Object. Classes estáticas não podem conter um construtor de instância; no entanto, eles podem ter um construtor estático. Para obter mais informações, consulte Construtores estáticos (Guia de programação em C #).


-1

Quando criamos uma classe estática que contém apenas os membros estáticos e um construtor privado. O único motivo é que o construtor estático impede que a classe seja instanciada, pelo que não podemos herdar uma classe estática. A única maneira de acessar o membro do classe estática usando o próprio nome da classe. Tente herdar uma classe estática não é uma boa ideia.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.