none
Envio Objeto Método C# RRS feed

  • Pergunta

  • Pessoal,

    estou aprendendo c# e desenvolvendo um projeto para o meu curso de cartografia, estou com um problema de passagem de um objeto para um método, no qual preciso realizar alguns cálculos sem modificar o objeto original, utilizado na passagem. Infelizmente não consigo isolar o mesmo toda vez, pois é um método iterativo. Devido meu código ser extenso, irei colocar um código simplificado para que entendam o meu caso. Desde já agradeço pela atenção de todos.

    class Coord
        {
            public double X { get; set; }
            public double Y { get; set; }
            public double Z { get; set; }
    
            public Coord(double x, double y, double z)
            {
                this.X = x;
                this.Y = y;
                this.Z = z;
            }
    
            public void imprimir()
            {
                Console.WriteLine("X = " + this.X + " Y = " + this.Y + " Z = " + this.Z);
            }
        }
    
    
    
    class Funcoes
        {
            //Diversas Funcoes
            public Coord Calculo(Coord XYZ)
            {
                for (Int16 i = 0; i < 10; i++)
                {
                    XYZ.X = XYZ.X - XYZ.Y - XYZ.Z;
                    XYZ.Y = XYZ.Y - XYZ.X - XYZ.Z;
                    XYZ.Z = XYZ.Z - XYZ.Y - XYZ.X;
                }
    
                return XYZ;
            }
        }
    
    class Program
        {
            static void Main(string[] args)
            {
                Coord coord1 = new Coord(10, 10, 10);
                coord1.imprimir();
                Funcoes func = new Funcoes();
    
                Coord coord2 = func.Calculo(coord1);
    
                coord1.imprimir();
                coord2.imprimir();
    
                Console.Read();
            }
        }

    Impressão Console:

    X = 10 Y = 10 Z = 10
    X = 25330 Y = 23710 Z = -83190
    X = 25330 Y = 23710 Z = -83190

    quinta-feira, 8 de fevereiro de 2018 23:30

Respostas

  • Olá renand1301!

       Parece que a sua classe "Calculo" só retorna 'por referência', independente se você pediu para ela retornar 'por valor'!

       Acredito que classes nunca retornem por valor, mas não achei algo explicando isso.

       Bom... acho que você não vai gostar disso, mas experimente...

       1 - Anexe essa nova função dentro de funções:

            public Coord Calculo2(double X, double Y, double Z)
            {
                Coord XYZ = new Coord(0,0,0); 
    
                for (Int16 i = 0; i < 10; i++)
                {
                    X = X - Y - Z;
                    Y = Y - X - Z;
                    Z = Z - Y - X;
                }
    
                XYZ.X = X;
                XYZ.Y = Y;
                XYZ.Z = Z;
                return XYZ;
            }

       2 - Na passagem de parâmetros (ficou feio) faça isso:

                double X = 10;
                double Y = 10;
                double Z = 10;
    
                Coord coord2 = func.Calculo2(X, Y, Z);

        Desculpe... não consegui nada melhor que isso...


    []'s,
    Fabio I.
    • Marcado como Resposta renand1301 sábado, 10 de fevereiro de 2018 12:31
    sexta-feira, 9 de fevereiro de 2018 11:34

Todas as Respostas

  • Olá renand1301!

       Parece que a sua classe "Calculo" só retorna 'por referência', independente se você pediu para ela retornar 'por valor'!

       Acredito que classes nunca retornem por valor, mas não achei algo explicando isso.

       Bom... acho que você não vai gostar disso, mas experimente...

       1 - Anexe essa nova função dentro de funções:

            public Coord Calculo2(double X, double Y, double Z)
            {
                Coord XYZ = new Coord(0,0,0); 
    
                for (Int16 i = 0; i < 10; i++)
                {
                    X = X - Y - Z;
                    Y = Y - X - Z;
                    Z = Z - Y - X;
                }
    
                XYZ.X = X;
                XYZ.Y = Y;
                XYZ.Z = Z;
                return XYZ;
            }

       2 - Na passagem de parâmetros (ficou feio) faça isso:

                double X = 10;
                double Y = 10;
                double Z = 10;
    
                Coord coord2 = func.Calculo2(X, Y, Z);

        Desculpe... não consegui nada melhor que isso...


    []'s,
    Fabio I.
    • Marcado como Resposta renand1301 sábado, 10 de fevereiro de 2018 12:31
    sexta-feira, 9 de fevereiro de 2018 11:34
  • Fabio, muito obrigado pela ajuda, me ajudou muito no entendimento de valores e referencias em C#. Estive em discussão sobre o mesmo problema em outro fórum e compartilho com você a ideia final obtida. Muito obrigado por tudo.

    ______________

    É comum ficarmos confusos em casos como o que vc apresenta em seu exemplo.

    No C# existem dois tipos principais: Tipos de Referência e de Valor. Classes, delegates e interfaces são tipos de referência. int, float, enum, decimal (e por ai vai) são tipos de valor. Eles funcionam de forma diferente. Referência é um tipo que não guarda o valor propriamente dito, mas uma referência àquele valor em um espaço na memória. Ao passo que o tipo Valor realmente guarda aquele valor em uma área da memória.

    Veja estas linhas, por exemplo:

    int x = 10; 
    Coord coord1 = new Coord(10, 10, 10);

    Quando dizemos que X = 10, o que ocorre por baixo dos panos? Uma área da memória é alocada para armazenar o valor 10. Int é um tipo de valor (Value Type).

    Mas e Quando damos um Coord coord1 = New Coord(10, 10, 10) ? Diferentemente do que ocorre com int, o objeto coord1 não guarda os valores de seus atributos. O que coord1 guarda é um endereço (uma referência) para o espaço da memória onde estão guardados os valores. 

    Apenas para exemplificar, digamos que este endereço da memória que coord1 guarda é 02A. É neste lugar da memória (um lugar chamado 02A) que estão armazenados os valores dos atributos etc.

    Matenha isso em mente.

    Veja agora o momento onde você invoca o método:

    Coord coord2 = func.Calculo(coord1);

    Quando você faz func.Calculo(coord1); o que você está passando para o método? 
    Você está passando um objeto guarda uma referência ao endereço 02A. (lembra ali em cima?)

    Agora observe a implementação do seu método Calculo (fiz comentários em algumas linhas para ficar mais claro):

    public Coord Calculo(Coord XYZ)//está recebendo um objeto que referencia um espaço na memória cujo endereço é 02A
            {
                for (Int16 i = 0; i < 10; i++)
                {
                    //Os cálculos são feitos nos atributos existentes no espaço na memória cujo endereço é 02A
                    XYZ.X = XYZ.X - XYZ.Y - XYZ.Z;
                    XYZ.Y = XYZ.Y - XYZ.X - XYZ.Z;
                    XYZ.Z = XYZ.Z - XYZ.Y - XYZ.X;
                }
      
                return XYZ; //Por fim, está devolvendo o mesmo objeto que guarda uma referência ao endereço 02A
            }
    

    Então finalmente: 

    coord1.imprimir(); //Invoca o método imprimir do objeto que referencia o endereço 02A
    coord2.imprimir(); //invoca o método imprimir do mesmo objeto que referencia pelo endereço 02A
    Portanto, apesar de serem duas variáveis. Tanto uma quanto a outra apontam para a mesma referência. Assim, modificando o valor de uma, o valor da outra é alterado também.

    Experimente modificar seu método para que ele fique assim:
    16
    public Coord Calculo(Coord XYZ)
           {
               Coord resultado = new Coord();
               resultado.X = XYZ.X;
               resultado.Y = XYZ.Y;
               resultado.Z = XYZ.Z;
     
               for (Int16 i = 0; i < 10; i++)
               {
                   resultado.X = resultado.X - resultado.Y - resultado.Z;
                   resultado.Y = resultado.Y - resultado.X - resultado.Z;
                   resultado.Z = resultado.Z - resultado.Y - resultado.X;
               }
     
               return resultado;
           }
    Fazendo um Coord resultado = new Coord(); estamos criando um novo objeto que guarda uma outra referência. Dessa forma, não afetamos o objeto original.

    A solução é simples, mas o conceito por trás disso é realmente complicado de entender. Sinta-se a vontade para entrar em contato novamente e tirar suas dúvidas.

    Um abraço e até mais!

    • Sugerido como Resposta Fabio I sábado, 10 de fevereiro de 2018 21:59
    sábado, 10 de fevereiro de 2018 12:38
  • renand1301,

       Você está coberto de razão! Eu já tinha me esquecido disso.
       Uma variável aloca a memória de uma forma diferente de uma classe. (Stack e Heap)

       Explicações mais detalhadas:

    ===============================

    Gerenciamento de memória no C#: stack, heap, value-types e reference-types
    https://www.treinaweb.com.br/blog/gerenciamento-de-memoria-no-c-stack-heap-value-types-e-reference-types/

    ===============================

    Entendendo Valores e Referências no C#
    https://msdn.microsoft.com/pt-br/library/cc517979.aspx

    ===============================

    Tipos e variáveis
    https://docs.microsoft.com/pt-br/dotnet/csharp/tour-of-csharp/types-and-variables

    ===============================

    Variáveis e tipos primitivos
    https://www.caelum.com.br/apostila-csharp-orientacao-objetos/variaveis-e-tipos-primitivos/

    ===============================

       Obrigado por me lembrar.

    []'s,
    Fabio I.
    sábado, 10 de fevereiro de 2018 21:59