Em C #, devo usar string.Empty ou String.Empty ou “” para inicializar uma string?


705

Em C #, quero inicializar um valor de sequência com uma sequência vazia.

Como devo fazer isso? Qual é o caminho certo e por quê?

string willi = string.Empty;

ou

string willi = String.Empty;

ou

string willi = "";

ou o que?


7
Veja também esta discussão semelhante para java: stackoverflow.com/questions/213985/…
harpo 4/08/08

39
melhor usar String.IsNullOrEmpty (string myString), certamente?
ZombieSheep

3
Eu uso [string.IsNullOrWhiteSpace (stringvalue)] ... funciona no .net 4.0. Para inicializar, eu simplesmente usar: [var text = "";] simples, legível e demora menos tempo para o tipo de :)
Jalal El-Shaer

61
O mais importante é o nome hilariante da sua variável.
Arj

5
O que me interessou é por que existe uma propriedade vazia. É bom e tudo, mas não é uma necessidade necessária e completa.
MasterMastic

Respostas:


808

Use o que você e sua equipe acharem mais legíveis.

Outras respostas sugeriram que uma nova string seja criada toda vez que você usar "". Isso não é verdade - devido à internação de strings, ele será criado uma vez por assembly ou uma vez por AppDomain (ou possivelmente uma vez para todo o processo - não tenho certeza dessa parte da frente). Essa diferença é insignificante - massivamente, massivamente insignificante.

Porém, o que você acha mais legível é uma questão diferente. É subjetivo e varia de pessoa para pessoa. Por isso, sugiro que você descubra como a maioria das pessoas da sua equipe gosta e que tudo isso seja consistente. Pessoalmente, acho ""mais fácil ler.

O argumento de que ""e " "é facilmente confundido com o outro realmente não lavar comigo. A menos que você esteja usando uma fonte proporcional (e eu não trabalhei com nenhum desenvolvedor que o faça), é muito fácil perceber a diferença.


75
Seus olhos podem enganá-lo quando você espera ver "" que você pode facilmente confundir "" com "". É por isso que é mais fácil editar algo que outra pessoa escreveu. Seu cérebro não tem idéias preconcebidas sobre o texto, por isso é mais fácil escolher as anonimas.
tvanfosson 4/11/08

125
@tvanfosson: Então você (ou um colega) realmente foi mordido por isso como um bug? Suspeito desse tipo de alegação sem que ele tenha realmente causado problemas. Estou usando o "" por anos sem nunca errar ...
Jon Skeet

34
Pessoalmente, sempre usei String.Empty, uso maiúsculas 'S' sempre que quero usar um método estático em string, é apenas uma preferência pessoal que me permite distinguir um tipo de uma variável. Mas isso é apenas uma transferência do uso de StringUtils.EMPTY no commons.lang do java. Um ponto de interesse é que estou quase cego e isso definitivamente ajuda na legibilidade para mim.
Brett Ryan

79
Você me deu a inspiração para começar a desenvolver no Times New Roman.
Justin Rusbatch 21/02

73
Por alguma razão obscura, string.Emptynão é uma constante . Isso significa que em vários casos em que é necessária uma constante em tempo de compilação, string.Emptyisso nem é legal. Isso inclui case ""blocos em switchinstruções, valores padrão de parâmetros opcionais , parâmetros e propriedades na aplicação de atributos e muitas outras situações (deixadas para o leitor). Portanto, considerando que isso string.Emptynão é permitido em algumas situações comuns, é melhor usar a ""convenção -everywhere.
Jeppe Stig Nielsen

375

Realmente não há diferença do ponto de vista de desempenho e código gerado. Nos testes de desempenho, eles iam e voltavam entre os quais era mais rápido do que o outro e apenas em milissegundos.

Ao olhar para o código dos bastidores, você também não vê nenhuma diferença. A única diferença está no IL, que string.Emptyusa o opcode ldsfld e ""o opcode ldstr, mas isso é apenas porque string.Emptyé estático, e as duas instruções fazem a mesma coisa. Se você observar a montagem produzida, é exatamente a mesma.

Código C #

private void Test1()
{
    string test1 = string.Empty;    
    string test11 = test1;
}

private void Test2()
{
    string test2 = "";    
    string test22 = test2;
}

Código IL

.method private hidebysig instance void 
          Test1() cil managed
{
  // Code size       10 (0xa)
  .maxstack  1
  .locals init ([0] string test1,
                [1] string test11)
  IL_0000:  nop
  IL_0001:  ldsfld     string [mscorlib]System.String::Empty
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  stloc.1
  IL_0009:  ret
} // end of method Form1::Test1
.method private hidebysig instance void 
        Test2() cil managed
{
  // Code size       10 (0xa)
  .maxstack  1
  .locals init ([0] string test2,
                [1] string test22)
  IL_0000:  nop
  IL_0001:  ldstr      ""
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  stloc.1
  IL_0009:  ret
} // end of method Form1::Test2

Código de montagem

        string test1 = string.Empty;
0000003a  mov         eax,dword ptr ds:[022A102Ch] 
0000003f  mov         dword ptr [ebp-40h],eax 

        string test11 = test1;
00000042  mov         eax,dword ptr [ebp-40h] 
00000045  mov         dword ptr [ebp-44h],eax 
        string test2 = "";
0000003a  mov         eax,dword ptr ds:[022A202Ch] 
00000040  mov         dword ptr [ebp-40h],eax 

        string test22 = test2;
00000043  mov         eax,dword ptr [ebp-40h] 
00000046  mov         dword ptr [ebp-44h],eax 

13
@PrateekSaluja: Para ver a IL, você pode usar o ildasm.exe, fornecido com o Visual Studio. Para ver a desmontagem, use a janela 'Desmontagem' no menu de depuração quando você atingir um ponto de interrupção (funciona também no código de liberação).
Thomas Bratt

1
Odeio que eu estou prestes a recomendar este produto. MAS .. o refletor permite que você escolha seu idioma ao desmontar a fonte e IL é uma opção! O ILDASM é um sentimento datado ... A equipe de ferramentas da MS parece não polir ou liberar as boas ferramentas!
Felickz

80

O melhor código é nenhum código :

A natureza fundamental da codificação é que nossa tarefa, como programadores, é reconhecer que toda decisão que tomamos é uma troca. […] Comece com brevidade. Aumente as outras dimensões conforme exigido pelo teste.

Consequentemente, menos código é melhor código: Prefere ""ao string.Emptyou String.Empty. Essas duas são seis vezes mais sem benefícios adicionais - certamente sem clareza adicional, pois expressam exatamente as mesmas informações.


1
mas em C # podemos apenas dizer string.IsNullOrWhitespace (s): p
felickz

31
Concordo que o código deve ser o menor possível, mas geralmente não argumentaria que menos caracteres é sempre um código melhor. Quando se trata de nomeação de variáveis, por exemplo, uma quantidade razoável de caracteres geralmente resulta em nomes melhores do que apenas usando iej.
Markus Meyer

3
@ Markus Isso depende muito: para uma variável de loop que representa um índice, i é melhor que um nome de variável longo. Ainda mais gerais, nomes de variáveis ​​mais curtos que transmitem a mesma informação , com a mesma clareza, são sempre preferíveis. É só que, para expressar as informações necessárias, você precisa de um certo comprimento de caractere e não estou negando isso (ninguém está).
Konrad Rudolph

2
@ Konrad: i é um bom nome de variável apenas se o loop for pequeno e não contiver outros índices. Mas eu concordo que se sth. pode ser declarado transmitindo mais brevemente as mesmas informações, que seriam preferíveis, como no caso string.Empty / "". A string.Empty não adiciona nenhuma clareza.
Markus Meyer

Para mim: string.Empty diz que esta string está e deve estar vazia o tempo todo, enquanto "" diz que, no momento da escrita, essa string pode estar vazia, mas você pode alterá-la.
aeroson

54

Uma diferença é que, se você usa uma switch-casesintaxe, não pode escrever case string.Empty:porque não é uma constante. Você recebe umCompilation error : A constant value is expected

Veja este link para obter mais informações: string-empty-versus-empty-quotes


22
A switchafirmação é um exemplo muito bom. Além disso, se você criar um parâmetro opcional, como void MyMethod(string optional = "") { ... }, também não será possível usá-lo string.Empty. E, claro, se você deseja definir um constcampo ou variável local, const string myString = "";novamente ""a única opção. Se ao menos string.Emptyhouvesse um campo constante, não haveria diferença. Mas não é, então, em alguns casos, você precisa usar "". Então, por que não usar ""o tempo todo?
Jeppe Stig Nielsen

5
Esse é um argumento muito forte, porque o uso string.Emptyimpede que você consiga consistência em sua base de código: você deve usar duas entidades diferentes para expressar a mesma coisa. E para adicionar à lista de coisas que você não pode fazer: você não pode usar string.Emptycom atributos .
Pragmateek

2
Muito bons pontos! O link está quebrado. Aqui está uma cópia do conteúdo: web.archive.org/web/20131230161806/http://kossovsky.net/…
ygoe 13/02/2015

42

Eu prefiro stringa String. escolher string.Emptysobre ""é uma questão de escolher um e ficar com ele. A vantagem de usar string.Emptyé que é muito óbvio o que você quer dizer, e você não copia acidentalmente caracteres não imprimíveis como "\x003"no seu "".


101
Eu diria que se você está copiando acidentalmente caracteres não imprimíveis em seu código, você tem problemas maiores do que esta pergunta;)
Jon Skeet

9
ASCII \ 003 passa a ser um delimitador de campo para mensagens B2B que eu trabalhei com :)
Jimmy

7
(Eu também sugiro evitar o \ x fuga, btw - é muito difícil de detectar a diferença entre "\ x9Bad Compiler" e "\ x9Good Compiler", que tem radicalmente diferentes resultados!)
Jon Skeet

Pessoalmente, eu prefiro String sobre string sempre que chamar um método estático em String. No entanto, sou quase cego e essa é uma preferência pessoal que não imponho a ninguém.
Brett Ryan

2
@ Jimmy Claro, mas estávamos conversando sobre a corda vazia. O argumento que ""é perigoso quando copiar / colar é nulo, afirmo, porque você nunca copia / cola a string vazia. Para outras strings, é sempre algo para se estar atento, é claro.
Timo

22

Eu não iria gritar, mas estou vendo algumas informações erradas sendo jogadas aqui.

Eu, pessoalmente, prefiro string.Empty. Essa é uma preferência pessoal, e eu me inclino à vontade de qualquer equipe com quem eu trabalhe, caso a caso.

Como alguns outros mencionaram, não há diferença entre string.Emptye String.Empty.

Além disso, e esse é um fato pouco conhecido, usar "" é perfeitamente aceitável. Todas as instâncias de "" criarão um objeto em outros ambientes. No entanto, o .NET armazena suas seqüências de caracteres, portanto, as instâncias futuras extrairão a mesma sequência imutável do pool de estagiários e qualquer ocorrência de desempenho será insignificante. Fonte: Brad Abrams .


20
Não vejo por que "tecnicamente" todas as instâncias de "" criarão um objeto. Não é apenas uma chance de que as strings sejam internadas - elas estão na especificação C #.
Jon Skeet

15

Eu pessoalmente prefiro "" a menos que haja uma boa razão para algo mais complexo.


13

String.Emptye string.Emptysão equivalentes. Stringé o nome da classe BCL; stringé o alias do C # (ou atalho, se você desejar). O mesmo que com Int32e int. Veja os documentos para mais exemplos.

No que diz ""respeito, não tenho muita certeza.

Pessoalmente, eu sempre uso string.Empty.


10

Quase todos os desenvolvedores sabem o que significa "". Eu pessoalmente encontrei o String.Empty pela primeira vez e tive que gastar algum tempo pesquisando no google para descobrir se eles realmente são exatamente a mesma coisa.


3
É um campo público de string somente leitura e seus valores são "" ... por que isso mudaria?
Matthew Whited

5
Você perdeu o argumento que @Jason faz. Como você sabe o que é a primeira vez que vê string.Empty? Você sabia qual ""foi a primeira vez que viu?
David R Tribble

10

Esse tópico é bastante antigo e longo, com licença, se esse comportamento tiver sido mencionado em outro lugar. (E me aponte para a resposta que cobre isso)

Eu encontrei uma diferença no comportamento do compilador se você usar string.Emptyou aspas duplas. A diferença é exibida se você não usar a variável string inicializada com string.Empty ou com aspas duplas.

Em caso de inicialização com string.Emptyo Aviso do Compilador

CS0219 - The variable 'x' is assigned but its value is never used

nunca é emitido enquanto, no caso de inicialização com aspas duplas, você obtém a mensagem esperada.

Esse comportamento é explicado no artigo Conectar neste link: https://connect.microsoft.com/VisualStudio/feedback/details/799810/c-warning-cs0219-not-reported-when-assign-non-constant-value

Basicamente, se eu entendi direito, eles querem permitir que um programador defina uma variável com o valor de retorno de uma função para fins de depuração sem incomodá-lo com uma mensagem de aviso, limitando o aviso apenas no caso de atribuições e seqüências de caracteres dispendiosas. Vazio não é uma constante, mas um campo.


1
Eu acredito que você é o primeiro a mencionar. Eu li estas perguntas e respostas há vários meses e não me lembro dessa diferença, o que faria se tivesse sido mencionada.
Palec

2
Interessante. Observe que uma declaração var unused = "literal";pode ser completamente otimizada (removida) pelo compilador. Não pode ter efeitos colaterais. Por outro lado, var unused = MyClass.Member;não pode ser removido completamente. Isso ocorre porque a leitura Memberpode ter efeitos colaterais. Se Memberfor uma propriedade estática com um getacessador, é claro que a chamada para o getter deve ser mantida. Mas mesmo que Memberseja um campo estático, pode haver o efeito colateral que o construtor estático pode executar. Claro que seria um estilo ruim de codificação tê-lo dessa maneira. Mas você precisa de um boneco para ler Member.
Jeppe Stig Nielsen

9

Eu executei esse teste muito simples usando o seguinte método em um aplicativo de console:

private static void CompareStringConstants()
{
    string str1 = "";
    string str2 = string.Empty;
    string str3 = String.Empty;
    Console.WriteLine(object.ReferenceEquals(str1, str2)); //prints True
    Console.WriteLine(object.ReferenceEquals(str2, str3)); //prints True
}

Isso sugere claramente que as três variáveis str1, a saber , str2e str3embora sejam inicializadas usando sintaxe diferente, estão apontando para o mesmo objeto de string (de comprimento zero) na memória. Eu executei este teste no aplicativo de console .NET 4.5. Portanto, internamente, eles não têm diferença e tudo se resume à conveniência de qual você deseja usar como programador. Esse comportamento da classe de string é conhecido como interning de string no .NET. Eric Lippert tem um blog muito bom aqui descrevendo esse conceito.


8

Qualquer uma das anteriores.

Há muitas, muitas coisas melhores para pontificar. Como a cor da casca que melhor se adapta a uma árvore, acho que marrom vago com reflexos de musgo doce.


7

Eu prefiro o String.Empty, além dos outros motivos para garantir que você saiba o que é e que não removeu acidentalmente o conteúdo, mas principalmente para a internacionalização. Se eu vejo uma string entre aspas, sempre tenho que me perguntar se esse código é novo e deve ser colocado em uma tabela de strings. Então, toda vez que o código é alterado / revisado, você precisa procurar "algo entre aspas" e sim, pode filtrar as cadeias vazias, mas digo às pessoas que é uma boa prática nunca colocar cadeias entre aspas, a menos que você saiba que não será localizado .


7

Ninguém mencionou que no VisualStudio String o código de cores é diferente do que o string. O que é importante para facilitar a leitura. Além disso, letras minúsculas geralmente são usadas para vars e tipo, não um grande problema, mas String.Empty é uma constante e não uma var ou tipo.


String.Empty não é uma constante: consulte stackoverflow.com/questions/507923/… Na verdade, é uma instância da classe String, por design. E coloração foi mencionado antes, embora um pouco escondido: stackoverflow.com/questions/263191/...
Michael

6

stringé sinônimo de System.Stringtipo, eles são idênticos.

Os valores também são idênticos: string.Empty == String.Empty == ""

Eu não usaria constantes de caracteres "" no código, string.Emptyou String.Empty- mais fácil ver o que significava programador.

Entre stringe Stringeu gosto mais de letras minúsculas, stringsó porque eu costumava trabalhar com Delphi por muitos anos e o estilo Delphi é minúsculo string.

Então, se eu fosse seu chefe, você estaria escrevendo string.Empty


6

Eu seria a favor de string.Emptymais String.Emptyporque você pode usá-lo sem a necessidade de incluir um using System;no seu arquivo.

Como para a colheita ""mais string.Empty, é preferência pessoal e deve ser decidida por sua equipe.


2
Eu sou o único membro da equipe, como eu decido? jogar um dado?
Gqqnbig

1
Para aqueles que podem estar se perguntando como é possível usar a string.Emptyconstante sem importar o using Systemespaço para nome - as palavras-chave em C # simplesmente são convertidas em seu nome completo, que inclui o espaço para nome antes de serem gravadas como MSIL na saída * .dll ou *. arquivo exe. Tão eficazmente string.Emptyé escrito como System.String.Emptyno MSIL pelo compilador. E como você já deve saber, se mencionar o nome completo do tipo, poderá ignorar a importação de espaços para nome na parte superior do seu arquivo de código.
RBT

5

Eu não faço diferença. O último é o mais rápido de digitar :)


4

Não importa - eles são exatamente a mesma coisa. No entanto, o principal é que você deve ser consistente

ps Luto com esse tipo de "qual é a coisa certa" o tempo todo.


1
No mundo moderno, "consistente" significa consistente em todas as equipes em todo o mundo, que é um dos objetivos do StackOverflow. Se eu puder sugerir, vamos usar String.Empty.
Pavel Radzivilovsky,

1
Alguns idiomas não possuem uma constante Vazia, e todos os idiomas em que consigo "" permitir uma string de comprimento zero. Então voto em "" por consistência com outros idiomas. :)
TomXP411

4

É totalmente uma preferência de estilo de código, como o .NET lida com seqüências de caracteres. No entanto, aqui estão as minhas opiniões :)

Eu sempre uso os nomes de tipo BCL ao acessar métodos, propriedades e campos estáticos: String.EmptyouInt32.TryParse(...) ouDouble.Epsilon

Eu sempre uso as palavras-chave C # ao declarar novas instâncias: int i = 0; oustring foo = "bar";

Eu raramente uso literais de cadeia não declarada, pois gosto de poder escanear o código para combiná-lo em constantes nomeadas reutilizáveis. O compilador substitui as constantes pelos literais de qualquer maneira, portanto é mais uma maneira de evitar números / seqüências mágicas e dar um pouco mais de significado a elas com um nome. Além disso, alterar os valores é mais fácil.


3

Eu uso o terceiro, mas dos outros dois o primeiro parece menos estranho. string é um alias para String, mas vê-los em uma atribuição parece errado.


3

Qualquer um dos dois primeiros seria aceitável para mim. Eu evitaria o último, porque é relativamente fácil introduzir um erro, colocando um espaço entre as aspas. Este bug em particular seria difícil de encontrar por observação. Supondo que não haja erros de digitação, todos são semanticamente equivalentes.

[EDITAR]

Além disso, você pode sempre usar uma stringou outra Stringconsistência, mas sou apenas eu.


Eu concordo com esta observação, mas ainda vivo perigosamente quando sou preguiçoso. De qualquer forma, acho que não tenho ocasião de escrever código que use uma string antes de atribuí-la fora da declaração da variável. De fato, é chato para mim ter que inicializar minhas strings, apesar dos riscos.
EnocNRoll - AnandaGopal Pardue

3

Eu testemunhei pessoalmente "" resultando em problemas (menores) duas vezes. Uma vez foi devido a um erro de um desenvolvedor júnior novo na programação baseada em equipes, e o outro foi um erro de digitação simples, mas o fato é o uso de string.Empty teria evitado os dois problemas.

Sim, essa é uma decisão de julgamento, mas quando um idioma oferece várias maneiras de fazer as coisas, eu tendem a me inclinar para aquele que tem a supervisão mais compiladora e a execução mais forte em tempo de compilação. Isso não é "". É tudo uma questão de expressar intenção específica.

Se você digitar string.EMpty ou Strng.Empty, o compilador informa que você fez isso errado. Imediatamente. Simplesmente não será compilado. Como desenvolvedor, você está citando assuntos específicos. intenção que o compilador (ou outro desenvolvedor) não possa interpretar mal de forma alguma e, quando você faz isso errado, não pode criar um bug.

Se você digitar "" quando quiser "" ou vice-versa, o compilador fará o que você pediu. Outro desenvolvedor pode ou não conseguir coletar sua intenção específica. Bug criado.

Muito antes da string.Empty, usei uma biblioteca padrão que definia a constante EMPTY_STRING. Ainda usamos essa constante nas instruções de caso em que string.Empty não é permitido.

Sempre que possível, coloque o compilador para trabalhar para você e elimine a possibilidade de erro humano, por menor que seja. Na IMO, isso supera a "legibilidade", como outros já citaram.

Especificidade e tempo de compilação. É o que tem para o jantar.


3

Eu uso "" porque será colorido em amarelo no meu código ... por algum motivo, String.Empty é todo branco no meu tema do Visual Studio Code. E acredito que isso é o mais importante para mim.


2

O compilador deve torná-los iguais a longo prazo. Escolha um padrão para que seu código seja fácil de ler e fique com ele.


2

Eu estava apenas olhando para algum código e essa pergunta surgiu em minha mente, que eu já havia lido algum tempo antes. Esta é certamente uma questão de legibilidade.

Considere o seguinte código C # ...

(customer == null) ? "" : customer.Name

vs

(customer == null) ? string.empty : customer.Name

Pessoalmente, acho o último menos ambíguo e mais fácil de ler.

Como apontado por outros, as diferenças reais são insignificantes.


1

Acho que o segundo é "adequado", mas para ser sincero, não acho que isso importe. O compilador deve ser inteligente o suficiente para compilar qualquer um deles exatamente no mesmo bytecode. Eu uso "" eu mesmo.


1

Embora a diferença seja muito, MUITO pequena, a diferença ainda existe.

1) "" cria um objeto enquanto String.Empty não. Mas este objeto será criado uma vez e será referenciado a partir do conjunto de cadeias posteriormente, se você tiver outro "" no código.

2) String e string são as mesmas, mas eu recomendaria usar String.Empty (assim como String.Format, String.Copy etc.), pois a notação de ponto indica classe, não operador, e a classe que começa com letra maiúscula está em conformidade com Padrões de codificação em C #.


1
string.Empty é "", verifique a fonte
dss539 12/11/2009

1

Em http://blogs.msdn.com/b/brada/archive/2003/04/22/49997.aspx :

Como David implica, há diferenças entre String.Emptye ""são bem pequenas, mas há uma diferença. ""na verdade, cria um objeto, provavelmente será retirado do pool interno de strings, mas ainda assim ... enquanto String.Emptynão cria nenhum objeto ... por isso, se você realmente estiver procurando por eficiência de memória, sugiro String.Empty. No entanto, você deve ter em mente que a diferença é tão rival que você nunca gostará de vê-la no seu código ...
Quanto a System.String.Emptyou string.Emptyou String.Empty... meu nível de atenção é baixo ;-)


1
A publicação no blog do MSDN foi em 2003 ... você tem certeza de que isso ainda é verdade para as versões recentes do .NET ?!
Carsten Schütte

@ CarstenSchütte: Parece-me que esse recurso não tem a intenção de mudar muito ... e, se foi, houve algum burburinho na Internet sobre isso.
Sergiol 29/04

3
@sergiol Se um campo é mais eficiente que um literal, isso é inequivocamente um bug de desempenho. Então, esperamos que isso seja corrigido agora.
Konrad Rudolph

1

A cadeia vazia é como um conjunto vazio, apenas um nome que todo mundo usa para chamar "". Também em linguagens formais, as strings criadas a partir de um alfabeto com comprimento zero são chamadas de strings vazias. O conjunto e a sequência possuem um símbolo especial. Cadeia vazia: ε e conjunto vazio: ∅. Se você quiser falar sobre essa string de comprimento zero, você a chamará de string vazia, para que todos saibam exatamente a que você está se referindo. Agora, caso você o nomeie como uma string vazia, por que não usar string.Emptyno código, isso mostra que a intenção é explícita. A desvantagem é que não é uma constante e, portanto, não está disponível em todos os lugares, como nos atributos. (Não é uma constante por alguns motivos técnicos, consulte a fonte de referência.)


0

Eu prefiro ""porque é mais curto e String.Emptyresolve um problema que não existe.

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.