Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[C#]Korzystanie z funkcji z innej klasy, tej samej namespace

andros1245 16 Gru 2010 17:40 13947 19
  • #1 16 Gru 2010 17:40
    andros1245
    Poziom 10  

    Witam!

    Zaczynam dopiero przygodę z C#. Mam kilka plików scalonych w jednym projekcie, o tej samej przestrzeni nazw, lecz różnych klasach. Jak w pliku skorzystać z funkcji znajdującej się w innej klasie w innym pliku? Przy okazji zapytam też, jak skorzystać ze zmiennej znajdującej się w innym pliku?

    Dodam jeszcze, że funkcje/procedury/zmienne mają status public.

    Proszę o pomoc.

    0 19
  • #2 16 Gru 2010 19:13
    dturczak
    Poziom 19  

    mozesz uzyc modyfikatora static dzieki czemu mozesz wywolac f/p/z przez klase a nie jej obiekt,ale to troche malo eleganckie..
    albo poprosto przekaz instancje danej klasy no przez konstruktor...
    (obiekty domyslnie sa przekazywane przez referencje)

    Code:
    class klasa1
    
    {
    public static int a;
    public int b;

    }
    class klasa2
    {
      klasa1 k1_ref;

      klasa2(klasa1 tmp)
      {
         k1_ref=tmp;
         System.out.print(klasa1.a);
         System.out.print(k1_ref.b);
      }
    }

    main()
    {
    klasa1.a=111;
    klasa1 k1=new klasa1();
    k1.b=222;
    klasa2 k2=new klasa2(k1);

    }


    poczytaj o wzorcach mvc,fasada...
    ulatwia Ci zycie :)

    0
  • #3 16 Gru 2010 19:18
    marcinj12
    Poziom 40  

    Trochę nagmatwane to pytanie :) Jeśli dobrze rozumiem, to masz dwa pliki z klasą A i klasą B, i chcesz w klasie A wywołać metodę (publiczną) a klasy B ?
    Jeśli tak, to - zakładając że klasa B nie jest tatyczna (static) - będąc w klasie A musisz utworzyć instancję klasy B i wywołać metodę, no.

    Code:

    KlasaB klasaB = new KlasaB();
    klasaB.WykonajMetode();


    Za zmiennych najlepiej korzystać w wykorzystaniem akcesorów (można też zmienną upublicznić, ale nie jest to wskazane :)):
    Code:

    public class KlasaB
    {
          public KlasaB()
          {
          }

          private int zminnaX;
          private int zmiennaY;

         //akcesory publiczne
         public int ZmiennaX { get { return zmiennaX; } set { zmiennaX = value; } }
        //tylko do odczytu
         public int ZmiennaY { get { return zmiennaY; }  }

    }


    //w klasie A:
    KlasaB klasaB = new KlasaB();
    klasaB.ZmiennaX = 10;
    int a = klasaB.ZmiennaY;

    0
  • #4 16 Gru 2010 20:29
    andros1245
    Poziom 10  

    marcinj12 napisał:
    Trochę nagmatwane to pytanie :) Jeśli dobrze rozumiem, to masz dwa pliki z klasą A i klasą B, i chcesz w klasie A wywołać metodę (publiczną) a klasy B ?
    Jeśli tak, to - zakładając że klasa B nie jest tatyczna (static) - będąc w klasie A musisz utworzyć instancję klasy B i wywołać metodę, no.
    Code:

    KlasaB klasaB = new KlasaB();
    klasaB.WykonajMetode();


    Za zmiennych najlepiej korzystać w wykorzystaniem akcesorów (można też zmienną upublicznić, ale nie jest to wskazane :)):
    Code:

    public class KlasaB
    {
          public KlasaB()
          {
          }

          private int zminnaX;
          private int zmiennaY;

         //akcesory publiczne
         public int ZmiennaX { get { return zmiennaX; } set { zmiennaX = value; } }
        //tylko do odczytu
         public int ZmiennaY { get { return zmiennaY; }  }

    }


    //w klasie A:
    KlasaB klasaB = new KlasaB();
    klasaB.ZmiennaX = 10;
    int a = klasaB.ZmiennaY;


    Dziękuję bardzo za pomoc :)
    Mam jednak pewien problem: klasa nie zawiera żadnych argumentów wejściowych i kompilator przy próbie new KlasaB() zwraca błąd:
    Code:
    'Program.KlasaB' does not contain a constructor that takes '1' arguments


    Deklaracja klasy:
    Code:
    class KlasaB

    W tym momencie nie mogę jej oflagować jako public, bo funkcje są podobne w obu klasach.

    0
  • #5 16 Gru 2010 20:34
    marcinj12
    Poziom 40  

    Wrzuć kod jak możesz, nie podajesz przypadkiem gdzieś argumentu w wywołaniu konstruktora klasy?
    Klasa niekoniecznie musi być oflagowana jako public, jeżeli nic nie podasz, kompilator domyślnie przypisze jej poziom internal, czyli klasę widoczną wewnątrz całego assembly (ale już nie poza - czyli np. nie dołączysz jej jako biblioteki .dll do innego projektu)

    0
  • #6 16 Gru 2010 20:53
    andros1245
    Poziom 10  

    Jest to kod z XNA lekko przerobiony:

    Code:
     class Gem
    
        {
            private Texture2D texture;
            private Vector2 origin;
            private SoundEffect collectedSound;
            private SoundEffect SuperMoc;

            public readonly int PointValue;
            public bool IsPowerUp { get; private set; }
            public readonly Color Color;


            private Vector2 basePosition;
            private float bounce;

            public Level Level
            {
                get { return level; }
            }
            Level level;

            public Vector2 Position
            {
                get
                {
                    return basePosition + new Vector2(0.0f, bounce);
                }
            }

            public Circle BoundingCircle
            {
                get
                {
                    return new Circle(Position, Tile.Width / 3.0f);
                }
            }

            public Gem(Level level, Vector2 position, bool isPowerUp)
            {
                this.level = level;
                this.basePosition = position;

                IsPowerUp = isPowerUp;
                if (IsPowerUp)
                {
                    PointValue = 100;
                    Color = Color.Red;
                }
                else
                {
                    PointValue = 30;
                    Color = Color.Yellow;
                }

                LoadContent();
            }





            public void LoadContent()
            {
                texture = Level.Content.Load<Texture2D>("Postacie/Diament");
                origin = new Vector2(texture.Width / 2.0f, texture.Height / 2.0f);
                collectedSound = Level.Content.Load<SoundEffect>("Sounds/Zdobyty");
                SuperMoc = Level.Content.Load<SoundEffect>("Sounds/Moc_efekt");
            }

            public void Update(GameTime gameTime)
            {
                const float BounceHeight = 0.18f;
                const float BounceRate = 3.0f;
                const float BounceSync = -0.75f;
               
                double t = gameTime.TotalGameTime.TotalSeconds * BounceRate + Position.X * BounceSync;
                bounce = (float)Math.Sin(t) * BounceHeight * texture.Height;
            }

            public void OnCollected(Player collectedBy)
            {
                collectedSound.Play();
               
             
                if (IsPowerUp)
                {
                    collectedBy.PowerUp();
                }
            }

            public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
            {
                spriteBatch.Draw(texture, Position, null, Color, 0.0f, origin, 1.0f, SpriteEffects.None, 0.0f);
            }
        }

    0
  • #7 16 Gru 2010 21:36
    marcinj12
    Poziom 40  

    OK, ale w którym miejscu masz komunikat o błędzie?
    Poza tym piszesz że masz kod w dwóch klasach / plikach, a to jest jedna klasa.

    0
  • #8 17 Gru 2010 06:33
    andros1245
    Poziom 10  

    Code:
    Gem diamenty = new Gem();

    Chodzi o ten nawias. Bez niego nie da się skompilować, ale z nim też nie.

    Code:
    public class PlatformerGame : Microsoft.Xna.Framework.Game
    
        {
            Gem diamenty = new Gem();


    Oto początek klasy, z której chcę wywołać drugą.

    0
  • #9 17 Gru 2010 09:35
    directx11
    Poziom 17  

    Code:
    public Gem(Level level, Vector2 position, bool isPowerUp)
    
    {
      this.level = level;
      this.basePosition = position;
      ...
    }


    Gdy tworzysz obiekt klasy Gem zostanie wywołany zostanie konstruktor (to na górze). On ma parametry, których nie podałeś w wywołaniu:

    Code:
    Gem diamenty = new Gem();


    Trzeba podać przy wywołaniu obiekty, których konstruktor potrzebuje, czyli wcześniej stworzyć obiekt klasy Level, Vector2 i bool i przekazać je jako parametry przy powyższym wywołaniu.

    0
  • #10 18 Gru 2010 13:03
    andros1245
    Poziom 10  

    Wszystko się zgadza!
    Dziękuję :)

    Jeszcze jedno pytanie:
    Mam klasę:

    Code:
    public class Diamenty
    
        {
            public Diamenty()
            {
            }
            public int diamenciki;
            public int Diament { get { return diamenciki; } set { diamenciki = value; } }

            public void Dodaj()
            {
                diamenciki++;
            }

        }

    i wywołuję ją gdzieś tam:
    Code:
    Diamenty dia = new Diamenty();
    
    dia.Dodaj();

    Niestety, po tej operacje obydwie zmienne w klasie mają wartość 0, co jest nie tak? Co robię źle? Dodam, że jeśli na sztywno przypiszę wartość dla zmiennej diamenciki to ta sztywna wartość się wyświetla, ale dalej nie chce inkrementować.

    Proszę o pomoc.

    0
  • #11 18 Gru 2010 13:57
    marcinj12
    Poziom 40  

    Zmienna diamenciki powinna być chyba typu prywatnego? Nie ma sensu pisać akcesorów

    Code:
    public int Diament { get { return diamenciki; } set { diamenciki = value; } } 
    do obiektu publicznego, skoro możesz w tej chwili odwołać się bezpośrednio do pola publicznego diamencik...

    Zmienne po wykonaniu operacji Dodaj() powinny się inkrementować prawidłowo.
    Przetestuj taki kod:
    Code:

    Diamenty dia = new Diamenty();
    dia.Dodaj();
    MessageBox.Show(dia.Diament.ToString());
    dia.Dodaj();
    MessageBox.Show(dia.Diament.ToString());
    dia.Dodaj();
    MessageBox.Show(dia.Diament.ToString());


    Czy Ty aby nie powołujesz za każdym razem nowej instancji klasy Diamenty ? Jeżeli chcesz żeby klasa "trzymała" stare wartości, musisz utworzyć obiekt tylko jeden raz:
    Code:
    Diamenty dia = new Diamenty();
    i potem cały czas się do niego odwoływać. Utworzenie nowej instancji tworzy nowy obiekt, z domyślnie inicjowanymi zmiennymi (int = 0).

    0
  • #12 18 Gru 2010 14:37
    andros1245
    Poziom 10  

    Faktycznie, w jednym pliku ciągle tworzyłem tą klasę.

    Wciąż pozostaje problem z inkrementacją w innym pliku, przykład:
    mam plik A i B, w obydwu tworzę na samym początku instancję. W pliku B inkrementuję zmienną, ale w pliku A już tego nie widzi.

    Jak to zrobić?

    0
  • #13 18 Gru 2010 14:47
    marcinj12
    Poziom 40  

    Przykład, przykład, przykład... :)
    Jeżeli oczekujesz konkretnej wskazówki, wrzuć konkretne przykłady kodu. Wklej to, co nazywasz plikiem A, to co nazywasz plikiem B i wskaż miejsce gdzie występuje błąd. Powodów może być mnóstwo, bez konkretnego kodu nie sposób pomóc, można co najwyżej "gdybać".

    0
  • #14 18 Gru 2010 16:02
    andros1245
    Poziom 10  

    Plik A, w którym jest klasa:

    Code:
     public class Diamenty
    
        {
            public Diamenty()
            {
            }
            public int diamenciki;

            public void Dodaj()
            {
                diamenciki++;
            }

        }

    public class PlatformerGame : Microsoft.Xna.Framework.Game
    {
    Diamenty dia = new Diamenty();
    }

    private void DrawHud()
    {
    DrawShadowedString(hudFont, "Diamenty: " + dia.diamenciki.ToString(), hudLocation += new Vector2(0.0f, timeHeight * 1.2f), Color.Yellow);
    }
    }


    Plik B, w którym jest inkrementacja:
    Code:

    class Gem
    {
    Diamenty dia = new Diamenty();

    public void OnCollected(Player collectedBy)
    {
    dia.Dodaj();
    }
    }


    Niestety tego, co zostało zainkrementowane w B, nie widzi w pliku A :(

    0
  • #15 18 Gru 2010 16:49
    directx11
    Poziom 17  

    Tak to nie zadziała. W jednym i w drugim pliku tworzysz dwa oddzielne obiekty tej samej klasy, ale są to oddzielne "byty", każdy żyje swoim życiem. Jak inkrementujesz coś w jednym to drugi tego nie widzi. Są dwa rozwiązania tego problemu. Ale obydwa obiekty będą posiadać pewną wspólną część (pole typu "static"), albo obiekt stworzony w jednym pliku będziesz przekazywał do drugiego i tam wywoływał jego metody.

    0
  • #16 18 Gru 2010 17:05
    marcinj12
    Poziom 40  

    Tak jak kolega directx11 napisał, tworysz dwie niezależne instancje.

    Jeżeli chcesz żeby klasa Gem inkrementowała istniejący obiekt typu Diamenty, musisz go przekazać do klasy Gem np. przez konstruktor, tak jak argument przekazujesz do funkcji. Domyślnie przekazujesz referencję do klasy, więc inkrementując w klasie B de facto działąsz na obiekcie z plasy A.

    Przyjrzyj się temu uproszczonemu przykładowi:

    Code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace WindowsFormsApplication1
    {
       public partial class Form1 : Form
       {
          public Form1()
          {
             InitializeComponent();
          }

          private void button1_Click(object sender, EventArgs e)
          {
             Diamenty dia = new Diamenty();

             dia.Dodaj();   //"zwykłe" zwiększenie
             MessageBox.Show(dia.diamenciki.ToString());   //tutaj = 1

             Gem gem = new Gem(dia);   //przekazujesz obiekt dia do klasy Gem
             gem.OnCollected();  //zwiększenie metodą z klasy Gem

             MessageBox.Show(dia.diamenciki.ToString());   //tutaj = 2
          }


       }

       public class Diamenty
        {
            public Diamenty()
            {
            }

            public int diamenciki;

            public void Dodaj()
            {
                diamenciki++;
            }

        }

       class Gem
       {
          Diamenty dia;
          public Gem(Diamenty dia)    //przekazujesz przez konstruktor instancję do klasy dia
          {
             this.dia = dia;
          }

          public void OnCollected()
          {
             dia.Dodaj();
          }
       }
    }

    0
  • #17 18 Gru 2010 19:25
    andros1245
    Poziom 10  

    marcinj12, jak najbardziej masz rację, ale to rozwiązanie jest tylko dobre na jeden plik :(
    Powiedzmy w pliku A stworzę instancję diamentów, a potem instację "przekaźnika" z parametrem diamentów, ale nie da się go przekazać do drugiego pliku, bo musiałbym tworzyć nowe instancje diamentów, co jest niemożliwe ze względu na brak przesyłu danych. Musiałbym stworzyć dwie zmienne, co dyskwalifikuje sposób. Błędne koło się zamyka. Nie mogę sobie ot tak wywoływać metod klasy Gem, bo to jest "zdarzenie". Czy jest jakiś inny sposób na przekazanie tego?

    Ta metoda ze static - jak ją wykonać?

    0
  • #18 19 Gru 2010 00:10
    marcinj12
    Poziom 40  

    Zapewniam, że to działa też przy podziale na dwa pliki. To, że klasa Gem w przykładzie jest w tym samym pliku nie ma znaczenia - równie dobrze mogła być w innym.
    Załączam Ci projekt (VS 2008) gdzie klasa Diamenty jest w osobnym pliku (PlikA), klasa Gem jest w osobnym pliku (PlikB) i do tego jest forma (Form1) ilustrująca działanie. Wszystko zależy od tego, w którym miejscu utworzysz instancję klasy Diamenty (w przykładzie: tylko raz, w konstruktorze formy). Podejrzyj te 3 pliki w projekcie.

    0
  • #19 19 Gru 2010 07:19
    andros1245
    Poziom 10  

    andros1245 napisał:
    Nie mogę sobie ot tak wywoływać metod klasy Gem, bo to jest "zdarzenie".

    Jak już napisałem - nie mogę ruszać tej klasy zwłaszcza, że nie mam argumentów do uruchomienia jej. Nie problemem jest dopisać jeden argument do klasy, ale problemem jest jej uruchomienie w odpowiednim momencie i z odpowiednimi argumentami, dlatego muszę wywoływać go z drugiego pliku, kiedy jest na to czas i odpowiedni argument. Nie mogę tego zrobić, bo jeśli wywołam instancję w drugim pliku na nowym obiekcie Diamenty to będzie katastrofa. Ogólnie chodzi o to, że nie mogę wywoływać metody Gem z pierwszego pliku.

    0
  • #20 19 Gru 2010 13:27
    marcinj12
    Poziom 40  

    OK, chyba rozumiem o co Ci chodzi.
    Czyli chcesz odwoływać się do zmiennej z klasy Diament, bez przekazywania jej do pliku ("globalnie")?

    W takim razie można by próbować zastosować wzorzec Singleton, albo - żeby nie komplikować - jak Koledzy radzili, użyć zwykłej statycznej klasy.

    Klasa statyczna nie musi być inicjowana, istnieje "od początku" uruchomienia programu i można się do niej odwoływać z każdego miejsca w programie.

    W tym wypadku klasa Diamenty oraz jej pola i metody musiały by być statyczne (bez konstruktora):

    Code:
    public static class Diamenty
    
    {
        public static int diamenciki;

        public static void Dodaj()
        {
            diamenciki++;
        }
    }


    I wtedy możesz w dowolnym pliku się odwołać do niej pisząc np.
    Code:
    Diamenty.Dodaj()


    Czyli w klasie Gem miałbyś:
    Code:
    class Gem
    
    {
        public void OnCollected(Player collectedBy)
        {
            Diamenty.Dodaj();
        }
    }

    0
  Szukaj w 5mln produktów