Tipo de Enum do CLR
A declaração e o comportamento de enums mudou de Managed Extensions for C++ para Visual C++ 2010.
A declaração de enum extensões gerenciadas é precedida pela __value palavra-chave. A idéia aqui é para distinguir o enum nativo de enum CLR que é derivado de System::ValueType, while sugerindo uma funcionalidade semelhante. For example:
__value enum e1 { fail, pass };
public __value enum e2 : unsigned short {
not_ok = 1024,
maybe, ok = 2048
};
A nova sintaxe resolve o problema de distinguir nativo e o CLR enums enfatizando a natureza de classe do último vez suas raízes do tipo de valor. Como tal, o __value palavra-chave é descartado, substituído com o par de palavra-chave espaçados de enum class. Isso fornece uma simetria de pares de palavra-chave para as declarações da referência, valor e classes de interface:
enum class ec;
value class vc;
ref class rc;
interface class ic;
A tradução do par de enumeração de e1 e e2 a nova sintaxe procura da seguinte maneira:
enum class e1 { fail, pass };
public enum class e2 : unsigned short {
not_ok = 1024,
maybe, ok = 2048
};
Além dessa pequena alteração sintática, o comportamento do tipo enum CLR foi alterado de várias maneiras:
- Uma declaração de reenvio de um enum CLR não é mais suportada. Não há nenhum mapeamento. Ele simplesmente será sinalizado como um erro em tempo de compilação.
__value enum status; // Managed Extensions: ok
enum class status; // new syntax: error
A resolução de sobrecarga entre os tipos de aritméticas internos e a Object a hierarquia de classe inverteu entre as versões de idioma de dois! Como um efeito colateral CLR enums não implicitamente são convertidos para tipos de aritméticos.
A nova sintaxe, um enum CLR mantém seu próprio escopo, o que não é o caso de Managed Extensions. Anteriormente, os enumeradores eram visíveis dentro do escopo que contém o enum. Agora, os enumeradores são encapsulados dentro do escopo de enum.
CLR Enums são um tipo de objeto
Considere o fragmento de código a seguir:
__value enum status { fail, pass };
void f( Object* ){ Console::WriteLine("f(Object)\n"); }
void f( int ){ Console::WriteLine("f(int)\n"); }
int main()
{
status rslt = fail;
f( rslt ); // which f is invoked?
}
Para o programador de C++ nativo, o natural responder à pergunta de qual instância do sobrecarregado f() é invocado é de f(int). Um enum é uma constante de integral simbólica e ela participa padrão promoções integrais que prevalecem nesse caso. E, na verdade no Managed Extensions essa era a instância para o qual a chamada resolve. Isso causou um número de surpresas – não quando usamos-los em um nativo C++ de espírito – mas precisávamos para interagir com a estrutura existente de BCL (Base Class Library), onde uma Enum é uma classe que deriva indiretamente Object. No Visual C++ 2010 o design de linguagem, a instância do f() invocado é o de f(Object^).
A maneira de Visual C++ 2010 optou por impor isso é não oferecer suporte a conversões implícitas entre um tipo enum do CLR e tipos de aritmética. Isso significa que qualquer atribuição de um objeto de um tipo de enum do CLR para um tipo de aritmético exigirá uma conversão explícita. Assim, por exemplo, dada
void f( int );
como um método sobrecarregado não no Managed Extensions, a chamada
f( rslt ); // ok: Managed Extensions; error: new syntax
está ok e o valor contido em rslt implicitamente é convertido em um valor inteiro. Em Visual C++ 2010, essa chamada falha a compilação. Traduzi-la corretamente, podemos deve inserir um operador de conversão:
f( safe_cast<int>( rslt )); // ok: new syntax
O escopo do tipo Enum CLR
Uma das alterações entre as linguagens c e C++ foi a inclusão em C++ do escopo do recurso de struct. Em C, uma estrutura é apenas um agregado sem suporte a uma interface ou um escopo associado de dados. Isso foi uma grande alteração radical no momento e foi uma questão contenciosos para muitos usuários de C++ novos proveniente de linguagem C. A relação entre o nativo e o CLR enum é análoga.
Extensões gerenciadas, foi feita uma tentativa para definir os nomes injetados levemente para os enumeradores de um enum CLR para simular a ausência de escopo dentro do enum nativo. Isso prova bem-sucedida. O problema é que isso faz com que os enumeradores derrubar no namespace global, resultando em difíceis de gerenciar os conflitos de nome. Na nova sintaxe, podemos ter acordo com os CLR em outros idiomas com suporte a escopos dentro do CLR de enum.
Isso significa que qualquer uso não-qualificado de um enumerador de um enum CLR não será reconhecido pela nova sintaxe. Vejamos um exemplo do mundo real.
// Managed Extensions supporting weak injection
__gc class XDCMake {
public:
__value enum _recognizerEnum {
UNDEFINED,
OPTION_USAGE,
XDC0001_ERR_PATH_DOES_NOT_EXIST = 1,
XDC0002_ERR_CANNOT_WRITE_TO = 2,
XDC0003_ERR_INCLUDE_TAGS_NOT_SUPPORTED = 3,
XDC0004_WRN_XML_LOAD_FAILURE = 4,
XDC0006_WRN_NONEXISTENT_FILES = 6,
};
ListDictionary* optionList;
ListDictionary* itagList;
XDCMake() {
optionList = new ListDictionary;
// here are the problems …
optionList->Add(S"?", __box(OPTION_USAGE)); // (1)
optionList->Add(S"help", __box(OPTION_USAGE)); // (2)
itagList = new ListDictionary;
itagList->Add(S"returns",
__box(XDC0004_WRN_XML_LOAD_FAILURE)); // (3)
}
};
Cada um dos três não qualificados usa os nomes de enumerador ((1), (2), e (3)) precisará ser qualificados na conversão para a nova sintaxe para que o código-fonte para compilação. Aqui está uma tradução correta do código fonte original:
ref class XDCMake {
public:
enum class _recognizerEnum {
UNDEFINED, OPTION_USAGE,
XDC0001_ERR_PATH_DOES_NOT_EXIST = 1,
XDC0002_ERR_CANNOT_WRITE_TO = 2,
XDC0003_ERR_INCLUDE_TAGS_NOT_SUPPORTED = 3,
XDC0004_WRN_XML_LOAD_FAILURE = 4,
XDC0006_WRN_NONEXISTENT_FILES = 6
};
ListDictionary^ optionList;
ListDictionary^ itagList;
XDCMake() {
optionList = gcnew ListDictionary;
optionList->Add("?",_recognizerEnum::OPTION_USAGE); // (1)
optionList->Add("help",_recognizerEnum::OPTION_USAGE); //(2)
itagList = gcnew ListDictionary;
itagList->Add( "returns",
_recognizerEnum::XDC0004_WRN_XML_LOAD_FAILURE); //(3)
}
};
Isso altera a estratégia de design entre um nativo e um enum do CLR. Com um enum CLR, mantendo um escopo associado em Visual C++ 2010, é necessário nem eficaz encapsular a declaração de enum dentro de uma classe. Este idioma evoluiu perto da hora do cfront 2.0, na Bell Laboratories também para solucionar o problema da poluição de nome global.
Na versão original do beta da nova biblioteca iostream por Schwarz de Jerry na Bell Laboratories, a Jerry não encapsular todos os enums associados definidos para a biblioteca e os enumeradores comuns, como read, write, appende assim por diante, tornou quase impossível para os usuários compilar seu código existente. Uma solução teria sido desconfiguram os nomes, como io_read, io_write, etc. Uma segunda solução seria modificar o idioma, adicionando o escopo para um enum, mas isso não era praticável de acordo com o momento. A solução intermediária foi encapsular o enum dentro da classe ou classe de hierarquia, onde o nome da marca e os enumeradores do enum preencher o escopo de classe de delimitador.) Ou seja, a motivação para colocar enums nas classes de, pelo menos originalmente, não era filosóficas, mas uma resposta prática para o problema da poluição de espaço para nome global.
Com o Visual C++ 2010 enum, não há qualquer benefício atraente para encapsular um enum dentro de uma classe. Na verdade, se você olhar o System namespaces, você verá que habitam enumerações, classes e interfaces todos o mesmo espaço de declaração.