Mais

.net arcobjects draw polygon (retângulo) não pode começar no local preferido

.net arcobjects draw polygon (retângulo) não pode começar no local preferido


Estou usando o código de amostra (desenhar um polígono na tela) fornecido pela Esri, mas parece que não consigo iniciar o desenho de um local que desejo, toda vez que ele começa em qualquer lugar que a função (ou ferramenta) esteja localizada no mapa.

Alguém sabe desenhar um polígono conforme desejado? Algum exemplo aí?


Aqui está algo que eu coloquei junto com o link que você postou e outro link que você verá comentado no código. Ao clicar no mapa, ele desenhará unidades quadradas de 10x10 de onde você clicou. Eu trabalho em metros, então isso desenha um gráfico de tamanho razoável.

Crie um suplemento com uma ferramenta e adicione-o à classe.

sobrescrita protegida void OnMouseDown (MouseEventArgs arg) {IMap _map = ArcMap.Document.FocusMap; IScreenDisplay3 _display = (IScreenDisplay3) ArcMap.Document.ActiveView.ScreenDisplay; ESRI.ArcGIS.Geometry.IPoint _clickedPoint = _display.DisplayTransformation.ToMapPoint (arg.Location.X, arg.Location.Y); ESRI.ArcGIS.Display.IRgbColor rgbColor = new ESRI.ArcGIS.Display.RgbColorClass (); rgbColor.Red = 255; ESRI.ArcGIS.Display.IColor color = rgbColor; // Cast implícito. ESRI.ArcGIS.Display.ISimpleFillSymbol simpleFillSymbol = new ESRI.ArcGIS.Display.SimpleFillSymbolClass (); simpleFillSymbol.Color = color; ESRI.ArcGIS.Display.ISymbol symbol = simpleFillSymbol as ESRI.ArcGIS.Display.ISymbol; // Elenco dinâmico. ////http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#//0001000002wm000000 ESRI.ArcGIS.Geometry.IPointCollection4 _poly = (ESRI.ArcGIS.Geometry.IPointCollection4) ( new ESRI.ArcGIS.Geometry.Polygon ()); _poly.AddPoint (_clickedPoint, Type.Missing, Type.Missing); ESRI.ArcGIS.Geometry.IPoint _pnt = new ESRI.ArcGIS.Geometry.Point (); _pnt.PutCoords (_clickedPoint.X + 10, _clickedPoint.Y); _poly.AddPoint (_pnt, Type.Missing, Type.Missing); _pnt.PutCoords (_clickedPoint.X + 10, _clickedPoint.Y + 10); _poly.AddPoint (_pnt, Type.Missing, Type.Missing); _pnt.PutCoords (_clickedPoint.X, _clickedPoint.Y + 10); _poly.AddPoint (_pnt, Type.Missing, Type.Missing); _poly.AddPoint (_clickedPoint, Type.Missing, Type.Missing); _display.StartDrawing (_display.hDC, (System.Int16) ESRI.ArcGIS.Display.esriScreenCache.esriNoScreenCache); _display.SetSymbol (símbolo); _display.DrawPolygon (((ESRI.ArcGIS.Geometry.IPolygon) _poly)); _display.FinishDrawing (); base.OnMouseDown (arg); }

Comportamento de pixel de FillRectangle e DrawRectangle

Quase todas as vezes que uso Graphics.DrawRectangle ou Graphics.FillRectangle (as versões int), pareço perder os pixels nas bordas direita e inferior do retângulo que desejo desenhar.

Qual é a definição exata de quais pixels são preenchidos por

e há uma explicação lógica para isso? (para que eu possa finalmente lembrar o comportamento :). )

Usando o maravilhoso aplicativo Try GDI + de Oleg Zhuk, consegui encontrar uma curiosa diferença entre FillRectangle e DrawRectangle:

FillRectangle (brush, 0,0,1,1) desenha um único ponto em (0,0) que é de alguma forma compreensível (afinal é um retângulo com largura e altura).

DrawRectangle (pen, 0,0,1,1), por outro lado, desenha um pequeno retângulo de 2 por 2 pixels.

O mesmo comportamento é mostrado para outras combinações de largura / altura, DrawRectangle sempre estende um pixel a mais para a esquerda e para a parte inferior (o que é um pouco irritante, por exemplo, ao usar a propriedade ClientRectangle para desenhar um quadro em torno de um controle)

Minha primeira pergunta foi resolvida (Como as para eles se comportam. ) ainda resta o segundo: Por que eles se comportam dessa maneira?


Detecta a passagem do retângulo sobre o pixel amarelo

Tenho uma dúvida sobre a melhor abordagem para detectar quando um retângulo em movimento e potencialmente girado passa sobre um pixel amarelo da imagem de fundo de um painel.

Eu tenho um método que aceita uma imagem e um ponto e retorna verdadeiro se esse ponto for o de um pixel amarelo. Eu exijo essa detecção de cor para a função do meu jogo, que reinicializa o carro (jogador) se ele ultrapassar as bordas amarelas da pista. Este método é mostrado abaixo:

Anteriormente, para detectar se o retângulo do player passa sobre o amarelo, verifiquei a localização do retângulo, conforme fornecido pelos valores X e Y do objeto. O problema com isso é que a localização é o canto superior esquerdo de um retângulo horizontal, o que significa que o carro pode dirigir quase totalmente fora da pista sem ocorrer detecção.

Gostaria de corrigir isso verificando todos os pontos cobertos pelo retângulo. Isso não é tão simples quanto pode parecer, pois o retângulo provavelmente será girado. Meu desenho e lógica de movimento são mostrados abaixo:

Eu tentei, mas não consegui criar um método que traduz as coordenadas do canto e descobre o meio do retângulo, mas isso não funciona, e a detecção de amarelo dispara em lugares muito obscuros:

Eu realmente aprecio qualquer sugestão sobre como lidar com isso. Incluí a tradução e o código de detecção amarelo para o caso de estar a quilômetros de distância em minha tentativa e outra pessoa ter uma ideia melhor.


Métodos de localização

O método de localização é usado para especificar a qualidade de serviço desejada do gerente de localização. Por exemplo, um aplicativo de previsão do tempo com base em localização pode exigir informações relacionadas à localização apenas para distinguir uma cidade ou bairro, enquanto um aplicativo de navegação GPS pode exigir o nível de qualidade mais alto para apontar uma localização no mapa. A seleção do nível de qualidade apropriado não apenas ajuda o gerente do local a executar o sistema com eficiência, mas também proporciona uma boa experiência do usuário.

Usar a estrutura location_method_e (em aplicativos móveis e vestíveis) permite que seu aplicativo especifique os seguintes métodos de sistema de posicionamento de localização:

  • GPS, que usa o sistema de posicionamento global
  • WPS, que usa o sistema de posicionamento Wi-Fi
  • Híbrido, que seleciona o melhor método disponível no momento
  • Passivo, que recebe atualizações de localização passivamente
  • Fundido, que usa o método de localização fundido

Com base no método desejado, o gerenciador de localização fornece serviços baseados em localização de melhor esforço, como uma atualização de localização assíncrona ou notificação de monitoramento de região.


Classe Retângulo

Estou tentando criar uma biblioteca gráfica particular. Mesmo que eu esteja fazendo isso por diversão, gostaria de ter alguns comentários sobre minha codificação. Por exemplo, o que vocês acham da seguinte classe (Retângulo)? Ele tem algumas dependências de outras classes que não incluí aqui apenas para economizar espaço. Se vocês acham que eles são relevantes, deixe-me saber e vou editar a resposta para incluí-los.

As dependências do retângulo são:

Minhas perguntas mais importantes são:

  1. Se eu não permitir larguras ou alturas negativas, seus tipos devem ser mantidos? Estou usando int porque a maioria das variáveis ​​de "tamanho" em .net são int (em vez de uint). Ou seja: System.Collections.Generic.List's Conte como um int, mesmo que nunca possa ser negativo.
  2. É um projeto ruim para meu retângulo implementar IEnumerable? É contra-intuitivo?

Ah, e estou usando campos públicos somente leitura (em vez de obter apenas propriedades) para garantir que nem mesmo eu possa alterar seus valores por acidente.

Sobre algumas questões:

P: Por que você está implementando sua própria biblioteca gráfica? R: Comecei a brincar com isso quando estava tentando entender por que os métodos Get / Set do Bitmap do sistema são tão lentos. Acabei me divertindo muito brincando com "minha própria biblioteca" (mesmo que não fosse adequada para produção, já que era tão mal comentada e quase não tinha testes de unidade), então decidi continuar a brincar com isto.

P: Por que você está implementando seu próprio retângulo em vez de usar o do sistema? R: Em primeiro lugar, porque não gosto da ideia de um retângulo mutante. Não sou contra objetos mutáveis. Mas para "coisas realmente simples", como uma estrutura que representa um retângulo, acho que alterar uma de suas propriedades muda sua "essência". Portanto, devo criar um novo. Em segundo lugar, porque estou tentando reduzir as dependências de minha biblioteca no System.Graphics: Não parece certo criar uma biblioteca gráfica (que não traz nada de novo para a mesa) que depende de outra (ainda maior), certo ? Portanto, mesmo que seja um projeto divertido, estou tentando reduzir as dependências da minha biblioteca no System.Graphics. No momento, minha classe Bitmap depende fortemente da classe do Sistema para ler / gravar arquivos. E a maioria das minhas classes contém um método "ToSystemXXX". Mas, no futuro, se eu implementar uma maneira legítima de ler / gravar um bitmap em arquivos, poderia simplesmente remover esses métodos e ficar "livre" do System.Graphics.

P: Por que seu retângulo implementa IEnumerable? Um poço. Esse é um dos motivos pelos quais vim aqui: para obter feedback sobre isso. Meu raciocínio é que isso torna as outras classes um pouco mais enxutas. O bitmap, por exemplo, não precisa implementar um método "SetPixels (Rectangle, Color)", uma vez que já implementa um "SetPixels (IEnumerable, Color)". Mas estou recebendo uma sensação de "isso é muito pythônico para C # lib". Não tenho certeza se essa conveniência justifica sua implementação, pois está doendo. Não sei. A "semântica" do Retângulo.

P: Você roubou a implementação GetHashCode da Mycrosoft? R: Sim ._. como você pode ver no meu comentário GetHashCode.

Edit: Decidi que minhas "formas" não implementarão IEnumerable. Mesmo que seja bastante prático (e divertido) brincar com o IEnumerables, acho que não é uma boa escolha de design, neste caso. Principalmente porque não está imediatamente claro quais pontos você obterá ao iterar de uma forma que implementa IEnumerable. Nesse caso [retângulo], se você iterar sobre ele, espera obter os pontos de sua borda ou os que estão dentro dele? Ou ambos? Então sim. Vou refatorar meu código. Obrigado pelo feedback pessoal!


1 resposta 1

Seja $ P $ o seu polígono, $ e $ uma aresta de $ P $. Vou interpretar um aspecto de sua pergunta como buscando todos os retângulos máximos com um lado alinhado com cada $ e $, cada retângulo $ R $ máximo no sentido de que (a) $ R subconjunto P $ mas (b) não há outro retângulo $ R ' subconjunto P $ com $ R' supset R $. Se você puder resolver isso para uma aresta $ e $, poderá iterar sobre todas as $ n $ arestas de $ P $.

Fixe $ e $ para ser horizontal (girando $ P $). Agora, seu problema é encontrar os retângulos alinhados ao eixo máximos alinhados com $ e $. É claro que $ R $ é máximo apenas se os quatro lados de $ R $ não puderem ser movidos para aumentar $ R $. A aresta $ e $ determina um lado, portanto, há três a serem bloqueados por $ P $ na ampliação. Um lado de $ R $ pode ser bloqueado por um vértice de $ P $ situado em seu interior, ou dois lados podem ser bloqueados por seu canto comum apoiado em uma aresta de $ P $. Várias possibilidades são mostradas acima. Um algoritmo $ O (n ^ 3) $ ingênuo pode pesquisar todos os bloqueadores possíveis para os três lados, produzindo um algoritmo $ O (n ^ 4) $ sobre todas as arestas de $ P $. Dentre eles, você pode selecionar, por exemplo, aqueles com maior área ou maior perímetro ou qualquer critério que seja relevante para sua aplicação.

Esse algoritmo ingênuo pode ser consideravelmente acelerado. Uma série de artigos levou a um algoritmo $ O (n log ^ 2 n) $, que acho que ainda pode ser o melhor disponível. Se valeria a pena implementar todos os truques necessários para alcançar essa complexidade de baixo tempo é outra questão.

K. Daniels, V. Milenkovic e D. Roth. Encontrando o maior retângulo paralelo ao eixo da área em um polígono. Comput. Geom. Theory Appl., 7: 125-148, 1997. (ligação CiteSeer) Fig.1 mostrada acima.

Para abordar como "cortar continuamente [seu] polígono" requer alguns critérios para escolher entre todas as opções. O que descrevo acima é apenas um começo, identificando todos os retângulos máximos. Suspeito que qualquer escolha que você fizer levará a um problema NP-difícil, e você será forçado a heurísticas ou algoritmos de aproximação.

Uma heurística é o algoritmo ganancioso. Encontre, digamos, o retângulo máximo de maior área $ R $ em $ P $. Crie vários novos polígonos subtraindo $ R $: $ P setminus R $. Recurse nas peças. Isso seria fácil de implementar e igualmente fácil de impedir. :-)

Aqui está o que eu quis dizer com frustrar o algoritmo ganancioso:

Como nenhuma definição do que constitui uma cobertura ótima por esses retângulos foi detalhada, não posso provar que o algoritmo guloso falha em atingir a otimização. Mas estou convencido de que qualquer definição razoável de otimização nem sempre será alcançada pelo algoritmo ganancioso.


Inicie o instalador do ArcGIS Desktop 10. No Recursos de desenvolvedor do ArcGIS Desktop clique na seção Configurar para o ArcObjects SDK para Microsoft .NET Framework.

Assim que a instalação do SDK começar, você pode aceitar todos os padrões para concluir a instalação. No entanto, se qualquer um dos itens VCPP (Visual C ++) for pré-selecionado para instalação, você pode defini-los para não instalar conforme mostrado abaixo.

Após a conclusão da instalação do SDK, inicie o Visual Studio.

Uma instalação padrão do Visual Studio Express não permitirá que você escolha um local de pasta para armazenar seus arquivos de origem do projeto ao criar um novo projeto (eles são sempre armazenados em uma pasta raiz especificada nas opções). Este comportamento pode ser alterado abrindo o Opções diálogo encontrado no Ferramentas menu, selecionando o Projetos e Soluções item na lista e marcando a caixa ao lado de Salvar novos projetos quando criados.

Para verificar se o SDK foi integrado com sucesso ao IDE, selecione Novo Projeto & # 8230 de Arquivo cardápio. Expanda a lista à esquerda do Novo projeto diálogo e selecione o Suplementos de área de trabalho item para garantir que os modelos de add-in foram instalados conforme mostrado abaixo.

Clique Cancelar para fechar a caixa de diálogo.

Se os modelos não estiverem lá, tente reinstalar o SDK. Para mais soluções de problemas, pesquisar as postagens no ArcGIS Resource Center para & # 8220SDK installation & # 8221 é um bom lugar para começar.

Por último, verifique se os trechos de código estão disponíveis no Visual Studio. Selecione Gerenciador de trechos de código & # 8230 de Ferramentas cardápio. Verifique se há um ArcObjects item listado. Caso contrário, expanda para obter instruções sobre como adicioná-los (+/-)

Embora os fragmentos não sejam necessários, eles podem ser bastante úteis para operações comuns e pontos de partida para aprimoramento posterior. Para usar um snippet, clique com o botão direito em um ponto de inserção em seu código e selecione Inserir fragmento & # 8230 no menu de contexto. Você também pode navegar pelos trechos no Gerenciador de trechos de código para ver suas descrições.

# 1 por Ernie em 4 de maio de 2012 - 10:59

I & # 8217m usando VS10 Professional. Quando clico com o botão direito em meu código e seleciono & # 8220Insert Snippet & # 8221, as opções suspensas são Elemento, Esquema, Fragmento e xsd. Na tentativa de corrigir esse problema, tentei navegar do meu Code-Snippet-Manager para & # 8220Program Files Microsoft Visual Studio 9.0 Common 7 IDE VCSExpress Snippets ArcObjects & # 8221, mas encontre a pasta & # 8220VCSExpress & # 8221 não tem uma pasta & # 8220Snippets & # 8221 dentro dele. O mesmo é verdade se eu navegar no Micrsoft Visual Studio 10.0. Meu ArcMap SDK para .net está instalado. Reinstalei ele e o VS10 em todos os pedidos que pude. Eu reiniciei meu computador todas as vezes. as pastas necessárias simplesmente não são carregadas. Onde posso obter as pastas & # 8220Snippets / ArcObjects & # 8221 que supostamente residem na pasta VCSExpress para que eu possa simplesmente copiá-las para lá? Isso funcionará? Qualquer ajuda seria apreciada.

# 2 por qtools_gis em 8 de maio de 2012 - 10:34

Suspeito que os snippets estejam instalados, mas não para onde você está olhando.

Como você está usando o Visual Studio 2010 Professional, os snippets provavelmente estão instalados em um caminho diferente do observado acima para a edição Express do Visual Studio 2008. Talvez tente uma pesquisa de arquivo para um dos snippets comuns como & # 8220Cut Polygon.snippet & # 8221 e em seguida, use a caixa de diálogo Gerenciador de trechos para adicionar o caminho ao seu diretório principal ArcObjects.

Se isso não ajudar, você deve postar para obter ajuda no fórum ArcObjects & # 8211 All Development Languages ​​no site da ESRI.


ArcObjects e .NET - Episódio 22 - Refatoração de código

Muito obrigado por colocá-los juntos! I & # x27m voltando através deles e eles são todos muito bem feitos.

Que bom que pude ajudar vocês, é por isso que continuo fazendo esses vídeos para beneficiar a comunidade GIS e os geogeeks!

Aproveite e continue sugerindo coisas novas para fazer nos próximos episódios

Isso está um pouco fora do tópico do seu post, mas eu gostaria de saber se eu poderia perguntar o que você acha de ArcObjects vs ArcPy? Não encontrei muitas comparações entre os dois. Eu li que se tem mais controle usando ArcObjects, mas até agora não encontrei nada que apoiasse isso. Qualquer pensamento seria extremamente útil. Obrigado!

ArcObjects, que é a tecnologia COM subjacente de granulação fina que basicamente construiu ArcGIS. Este é o núcleo da tecnologia Desktop. Você pode construir literariamente seu PRÓPRIO ArcMap usando ArcObjects puros. Sim, é aquele Rich. Foi picado tão bem que você pode fazer quase qualquer coisa com ArcObjects. E foi estendido para ser codificado em .NET e Java. O problema é que é, bem, COM, então de 32 bits. (É por isso que a Esri está construindo a nova plataforma cruzada do ArcGIS de 64 bits, isso não é nem perto do ArcObjects com certeza, é meramente um SDK para processamento de serviços ArcGIS Online e ArcGIS, não pode funcionar com bancos de dados geográficos e coisas SDE como tanto quanto eu sei. Mas este é outro assunto)

Embora o ArcPy seja relativamente novo, ele começou a ser desenvolvido como uma abordagem de script para coisas de código, como processamento em lote e automação. Python é realmente simples e não requer IDE ou compilação para ser executado, os arquivos podem ser facilmente editados com o Bloco de notas, o que o tornou onipresente na comunidade GIS. A Esri começou a expor suas funções centrais como modelos de geoprocessamento. E com o construtor de modelos, você pode conectar diferentes modelos para fazer seu próprio modelo. A parte bonita é que você pode exportar qualquer modelo para python, o que injetou mais e mais funcionalidades no ArcPy. Digamos que você construiu um modelo que aceita uma classe de recurso, adiciona um novo campo, calcule este campo para adicionar alguns valores a ele.

Pessoalmente, eu não & # x27m um ArcPy & # x27er de núcleo duro, mas posso dizer que se uma funcionalidade GIS ainda não foi exposta como um modelo de geoprocessamento, você não pode fazê-lo no ArcPy. Pelo menos ainda não. Por exemplo, vamos pegar o ForwardStar, que é um algoritmo de rastreamento super rápido útil para seus elementos geométricos de rede. Isso é exposto no ArcObjects, mas não no ArcPy, existem outras interfaces também, com certeza.

Outra coisa sobre ArcPy é que você não pode personalizar (ou estender) a funcionalidade existente do ArcMap, digamos, por exemplo, que você deseja escrever uma ferramenta em cima do ArcMap que você pode clicar em um recurso no Mapa para conectar a uma geodatabase externa ou os arquivos simples recuperam alguns dados e os unem à camada de feição para exibi-los com o ArcMap. Você pode fazer isso com ArcObjects, mas não com ArcPy. Pode haver uma GUI para ArcPy, mas eu não conheço que a Esri tenha suporte.

Resumindo, acho que o ArcPy pode funcionar para você se você quiser escrever scripts, para dizer, preencher milhões de registros ou executar uma replicação em lote ou compactar seu banco de dados geográfico. No entanto, o ArcPy não é - mais uma vez - tão bom com a construção de um aplicativo. Se você deseja construir um aplicativo de desktop, um aplicativo completo que requer interação do usuário, GUI agradável, formulários, controles ricos, você tem que ir com .NET e ArcObjects, por exemplo.

Essa é minha opinião, pelo menos, novamente, eu só usei ArcPy para scripts até agora. Pode haver algumas pessoas que fizeram coisas legais com ele!


5 respostas 5

Quando usados ​​corretamente, enums são muito mais legíveis e robustos do que os "números mágicos" que eles substituem. Normalmente não os vejo tornando o código mais frágil. Por exemplo:

  • setColor () não precisa perder tempo verificando se o valor é um valor de cor válido ou não. O compilador já fez isso.
  • Você pode escrever setColor (Color :: Red) em vez de setColor (0). Eu acredito que o recurso de classe enum no C ++ moderno permite que você force as pessoas a sempre escreverem o primeiro em vez do segundo.
  • Normalmente não é importante, mas a maioria dos enums podem ser implementados com qualquer tipo de integral de tamanho, portanto, o compilador pode escolher o tamanho que for mais conveniente sem forçar você a pensar sobre essas coisas.

Contudo, usar um enum para a cor é questionável porque em muitas (na maioria?) situações não há razão para limitar o usuário a um conjunto tão pequeno de cores que você também pode deixá-los passar em quaisquer valores RGB arbitrários. Nos projetos com os quais trabalho, uma pequena lista de cores como essa só surgiria como parte de um conjunto de "temas" ou "estilos" que deveriam agir como uma abstração tênue sobre cores concretas.

Não tenho certeza de onde está chegando sua pergunta sobre "flexibilidade polimórfica". Enums não têm nenhum código executável, então não há nada para tornar polimórfico. Talvez você esteja procurando o padrão de comando?

Edit: Pós-edição, ainda não estou certo sobre que tipo de extensibilidade você está procurando, mas ainda acho que o padrão de comando é a coisa mais próxima que você encontrará de um "enum polimórfico".

Qualquer alteração na enumeração ColorChoice afeta todas as subclasses de IWindowColor.

Não, não faz. Existem dois casos: os implementadores irão

armazenar, retornar e encaminhar valores enum, nunca operando neles, caso em que eles não são afetados por mudanças no enum, ou

operar em valores de enum individuais, caso em que qualquer mudança na enum deve, naturalmente, naturalmente, inescapavelmente, necessariamente, ser contabilizado com uma mudança correspondente na lógica do implementador.

Se você colocar "Sphere", "Rectangle" e "Pyramid" em um enum "Shape" e passar esse enum para alguma função drawSolid () que você escreveu para desenhar o sólido correspondente, então, uma manhã, você decide adicione um valor "Ikositetrahedron" ao enum, você não pode esperar que a função drawSolid () permaneça inalterada. culpa. Então:

Enums tendem a causar interfaces quebradiças?

Não, eles não querem. O que causa interfaces frágeis são os programadores pensando em si mesmos como ninjas, tentando compilar seu código sem ter avisos suficientes habilitados. Em seguida, o compilador não os avisa que sua função drawSolid () contém uma instrução switch que não contém uma cláusula case para o valor enum "Ikositetrahedron" recém-adicionado.

A maneira como ele deve funcionar é análoga à adição de um novo método virtual puro a uma classe base: você então tem que implementar esse método em cada herdeiro, ou então o projeto não é, e não deve, ser construído.

Bem, verdade seja dita, enums não são uma construção orientada a objetos. Eles são mais um compromisso pragmático entre o paradigma orientado a objetos e o paradigma de programação estruturada.

A maneira puramente orientada a objetos de fazer as coisas é não ter enums, e sim ter objetos em todo o caminho.

Portanto, a maneira puramente orientada a objetos de implementar o exemplo com os sólidos é, obviamente, usar o polimorfismo: em vez de ter um único método centralizado monstruoso que sabe como desenhar tudo e ter que saber qual sólido desenhar, você declara um " Classe "sólida" com um método draw () abstrato (virtual puro) e, em seguida, você adiciona as subclasses "Sphere", "Rectangle" e "Pyramid", cada uma tendo sua própria implementação de draw () que sabe como desenhar a si mesma.

Dessa forma, ao introduzir a subclasse "Icositetrahedron", você apenas terá que fornecer uma função draw () para ela, e o compilador irá lembrá-lo de fazer isso, não permitindo que você instancie "Icositetrahedron" de outra forma.

Enums não criam interfaces quebradiças. Uso indevido de enums, sim.

Para que servem os enums?

Enums são projetados para serem usados ​​como conjuntos de constantes nomeadas de forma significativa. Eles devem ser usados ​​quando:

  • Você sabe que nenhum valor será removido.
  • (E) Você sabe que é altamente improvável que um novo valor seja necessário.
  • (Ou) Você aceita que um novo valor será necessário, mas raramente o suficiente para garantir a correção de todo o código que quebra por causa dele.

Bons usos de enums:

  • Dias da semana: (de acordo com System.DayOfWeek do .Net) A menos que você esteja lidando com um calendário incrivelmente obscuro, haverá apenas 7 dias da semana.
  • Cores não extensíveis: (de acordo com o System.ConsoleColor do .Net) Alguns podem discordar disso, mas o .Net optou por fazer isso por um motivo. No sistema de console do .Net, existem apenas 16 cores disponíveis para uso pelo console. Essas 16 cores correspondem à paleta de cores herdada conhecida como CGA ou 'Adaptador gráfico de cores'. Nenhum valor novo será adicionado, o que significa que esta é, na verdade, uma aplicação razoável de um enum.
  • Enumerações que representam estados fixos: (conforme Thread.State do Java) Os designers de Java decidiram que no modelo de threading Java haverá apenas um conjunto fixo de estados em que um Thread pode estar e, para simplificar, esses diferentes estados são representados como enumerações. Isso significa que muitas verificações baseadas em estado são ifs e opções simples que, na prática, operam em valores inteiros, sem que o programador precise se preocupar com quais valores realmente são.
  • Bitflags que representam opções não mutuamente exclusivas: (conforme System.Text.RegularExpressions.RegexOptions do .Net) Bitflags são um uso muito comum de enums. Tão comum na verdade, que em .Net todos os enums têm um método HasFlag (sinalizador Enum) integrado. Eles também suportam os operadores bit a bit e há um FlagsAttribute para marcar um enum como destinado a ser usado como um conjunto de bitflags. Usando um enum como um conjunto de sinalizadores, você pode representar um grupo de valores booleanos em um único valor, além de ter os sinalizadores claramente nomeados por conveniência. Isso seria extremamente benéfico para representar os sinalizadores de um registrador de status em um emulador ou para representar as permissões de um arquivo (ler, escrever, executar) ou qualquer situação em que um conjunto de opções relacionadas não sejam mutuamente exclusivas.

Maus usos de enums:

  • Classes / tipos de personagens em um jogo: A menos que o jogo seja uma demonstração única que provavelmente você não usará novamente, enums não devem ser usados ​​para classes de personagens, porque é provável que você deseje adicionar muito mais classes. É melhor ter uma única classe representando o personagem e ter um 'tipo' de personagem do jogo representado de outra forma. Uma maneira de lidar com isso é o padrão TypeObject; outras soluções incluem o registro de tipos de caracteres com um dicionário / registro de tipo ou uma mistura dos dois.
  • Cores extensíveis: Se você estiver usando um enum para cores que podem ser adicionadas posteriormente, não é uma boa ideia representá-lo na forma de enum, caso contrário, você ficará para sempre adicionando cores. Isso é semelhante ao problema acima, portanto, uma solução semelhante deve ser usada (ou seja, uma variação de TypeObject).
  • Estado extensível: Se você tem uma máquina de estados que pode acabar introduzindo muitos mais estados, não é uma boa ideia representar esses estados por meio de enumerações. O método preferido é definir uma interface para o estado da máquina, fornecer uma implementação ou classe que envolve a interface e delega suas chamadas de método (semelhante ao padrão Strategy) e, em seguida, altera seu estado alterando qual implementação fornecida está atualmente ativa.
  • Bitflags que representam opções mutuamente exclusivas: Se você estiver usando enums para representar bandeiras e duas dessas bandeiras nunca deveriam ocorrer juntas, então você deu um tiro no próprio pé. Qualquer coisa que esteja programada para reagir a uma das bandeiras reagirá repentinamente a qualquer flag programada para responder primeiro - ou pior, pode responder a ambas. Esse tipo de situação está apenas procurando problemas. A melhor abordagem é tratar a ausência de um sinalizador como a condição alternativa, se possível (ou seja, a ausência do sinalizador Verdadeiro implica em Falso). Esse comportamento pode ser suportado ainda mais por meio do uso de funções especializadas (ou seja, IsTrue (sinalizadores) e IsFalse (sinalizadores)).

Enums são uma grande melhoria em relação aos números de identificação mágicos para conjuntos fechados de valores que não possuem muitas funcionalidades associadas a eles. Normalmente, você não se preocupa com o número realmente associado ao enum, neste caso, é fácil estender adicionando novas entradas no final, nenhuma fragilidade deve resultar.

O problema é quando você tem uma funcionalidade significativa associada ao enum. Ou seja, você tem este tipo de código por aí:

Um ou dois deles está ok, mas assim que você tiver esse tipo de enum significativo, essas instruções switch tendem a se multiplicar como tribbles. Agora, toda vez que você estende o enum, você precisa procurar todos os switches associados para ter certeza de que cobriu tudo. Esta é frágil. Fontes como Código Limpo irá sugerir que você deve ter um switch por enum, no máximo.

O que você deve fazer neste caso é usar os princípios OO e criar um interface para este tipo. Você ainda pode manter o enum para comunicar este tipo, mas assim que precisar fazer algo com ele, você criará um objeto associado ao enum, possivelmente usando um Factory. Isso é muito mais fácil de manter, pois você só precisa procurar um local para atualizar: sua fábrica e adicionar uma nova classe.

Se você for cuidadoso ao usá-los, não considerarei enums prejudiciais. Mas existem algumas coisas a serem consideradas se você quiser usá-los em algum código de biblioteca, ao contrário de um único aplicativo.

Nunca remova ou reordene os valores. Se você, em algum momento, tiver um valor enum em sua lista, esse valor deve ser associado a esse nome por toda a eternidade. Se desejar, você pode em algum momento renomear os valores para deprecated_orc ou qualquer outra coisa, mas não removendo-os, você pode manter mais facilmente a compatibilidade com o código antigo compilado com o antigo conjunto de enums. Se algum código novo não puder lidar com constantes enum antigas, gere um erro adequado ou certifique-se de que nenhum valor atinja aquele trecho de código.

Não faça aritmética com eles. Em particular, não faça comparações de pedidos. Por quê? Porque então você pode encontrar uma situação em que não consiga manter os valores existentes e, ao mesmo tempo, preservar uma ordem sã. Tome, por exemplo, um enum para as direções da bússola: N = 0, NE = 1, E = 2, SE = 3, ... Agora, se após alguma atualização você incluir NNE e assim por diante entre as direções existentes, você pode adicioná-los ao final da lista e, portanto, quebrar a ordem ou intercalá-los com as chaves existentes e, assim, quebrar mapeamentos estabelecidos usados ​​no código legado. Ou você desaprova todas as chaves antigas e tem um conjunto de chaves completamente novo, junto com algum código de compatibilidade que traduz entre chaves antigas e novas por causa do código legado.

Escolha um tamanho suficiente. Por padrão, o compilador usará o menor inteiro que pode conter todos os valores enum. O que significa que se, em alguma atualização, seu conjunto de enums possíveis se expandir de 254 para 259, você precisará repentinamente de 2 bytes para cada valor de enum em vez de um. Isso pode quebrar a estrutura e os layouts de classe em todos os lugares, portanto, tente evitar isso usando um tamanho suficiente no primeiro design. C ++ 11 oferece muito controle aqui, mas, caso contrário, especificar uma entrada LAST_SPECIES_VALUE = 65535 também deve ajudar.

Tenha um registro central. Como você deseja corrigir o mapeamento entre nomes e valores, é ruim permitir que usuários terceirizados de seu código adicionem novas constantes. Nenhum projeto usando seu código deve ter permissão para alterar esse cabeçalho para adicionar novos mapeamentos. Em vez disso, eles devem incomodá-lo para adicioná-los. Isso significa que, para o exemplo de humano e anão que você mencionou, enums são de fato um ajuste inadequado. There it would be better to have some kind of registry at run-time, where users of your code can insert a string and get back a unique number, nicely wrapped in some opaque type. The “number” might even be a pointer to the string in question, it doesn't matter.

Despite the fact that my last point above makes enums a poor fit for your hypothetical situation, your actual situation where some spec might change and you'd have to update some code seems to fit in quite well with a central registry for your library. So enums should be appropriate there if you take my other suggestions to heart.


Line breaks

There is no need to add when calling Console.WriteLine . It will add a line break automatically.

Moreover, is not the correct method of adding a line break in .NET. Use Environment.NewLine instead.

The order of the final print in your example is wrong. It prints before actually assigning shape = new Rectangle() .

no, this isn't exactly correct. Your demonstration exhibits whether a type implements IShape.

The part that seems correct is that you can get the IShape interface from Rectangle type ( IShape shape = new Rectangle() ), because Shape implements IShape and Rectange inherits Shape .

However, Rectangle does not implement IShape , its base does.. so IShape shape = new Rectangle() shape.Draw() will be equivalent to ((Shape)shape).Draw() not ((Rectangle)shape).Draw() .

Additionally, you're overloading Shape.Draw by defining Rectangle.Draw , so your sample doesn't depict any inherited methods that satisify to usage..

To feature inheritance, you demonstrate the implicit and explicit castings as follows:

Then iterate over the List as so:

For the explicit casting example.

Additionally,

IShape.Draw is improper.. Shapes don't draw. they are drawable to be drawable indicates a context in which they are to be drawn on.

Interfaces can inherit other interfaces.. you should consider demonstrating that concept as well.

Class & Interface Definitions

Program

Output

A shorter demonstration. define an instance of the interface of the type that inherits all types, and explicitly cast .

Output

Your code doesn't benefit from the use of inheritance. You can remove all references to IShape without losing anything. If you had an illustrator class that accepted IShape objects, then you'd have something but it feels forced.

I tend to avoid inheritance. They say we should prefer composition over inheritance. One thing I look for in my code is switch statements and enums. I have found that if I replace them with polymorphism, my code becomes cleaner. I can add the equivalent of switch-cases without changing existing code.

Example using switch statements:

In this case, your code supports three FileTypes and your switch statement controls which method to call based on your FileType parameter. If you add a FileType "dat" to your enum, you have to add a case to each of your switch statements. This is error-prone if you have many switch cases as adding the "dat" value to the enum could be a breaking change. If you don't have access to source code that uses these enums your problem is exasperated.

Example using inheritance:

The benefits to inheritance here are that when you deal with a FileGenerator type, you don't have to use a switch statement containing a case for every type of FileGenerator. You might use a Factory like this:

Notice that you have no conditionals here. You don't care how the FileGenerator type is implemented, only that it has a GenerateFile(DataType) method. Your factory may even return an object that didn't exist when the abstract class FileGenerator was first implemented. An object in a different assembly and namespace. All you care is that this object satisfies the contract stating that it has a GenerateFile(DataType) method.

Note that I wrote this example in a way that you couldn't easily switch from using an abstract class to an interface. I've been asked the difference in job interviews before.


Assista o vídeo: How to Convert point to Polygon In ArcGIS