C# Klasa jako parametr

1

Witam! Jestem początkującym programistą C#. Istnieją typy A, B i C. W każdym z nich występują odpowiednio A.1, A.2, A.3, A.4, B.1 etc., w kórych istnieje po kilkanaście elemntów W mojej aplikacji mam pewien formularz, w którym najpierw wybiera się przez ComboBox1 typ (A, B lub C), a następnie poprzez zaznaczenie Check Boxów które podtypy (1, 2, 3 i 4). W ComboBox2 ma wyświetlić się kombinacja tych typów. Dokładnie: wszystkie elementy typu wybranego w ComboBox1, które występują tylko w podtypach wybranych w CheckBoxach.
Wiem że trochę chaotycznie to wyszło i ciężko będzie zrozumieć, więc zamieszczam tutaj taki oto przykładowy kod:

         public class A
        {
            public object[] a1, a2, a3, a4;
            public A()
            {
                a1 = new object[] { "1", "2", "3", "4", "5", "6" };
                a2 = new object[] { "3", "4", "5", "8", "12", "32", "128" };
                a3 = new object[] { "9", "10", "11", "12", "13", "14" };
                a4 = new object[] { "13", "14", "15", "16", "17" };
            }
        }
        public class B
        {
            public object[] a1, a2, a3, a4;
            public B()
            {
                a1 = new object[] { "q", "w", "e", "r", "t", "y" };
                a2 = new object[] { "q", "w", "e", "r", "x", "y", "z" };
                a3 = new object[] { "a", "s", "d", "f", "g", "h" };
                a4 = new object[] { "g", "h", "j", "k", "l" };
            }
        }

Teraz np. jeśli zaznaczę w A i tylko 1 (w Check Boxach) to w ComboBox2 chcę otrzymać: 1, 2, 6. Jeśli zaznaczę a3 i a4 to: 13, 14; natomiast, gdy a2, a3 i a4: null

Jak to zrobić? Napisałem coś takiego:

void zaladuj()
        {
            object[] items = new object[9000];
            int liczba = 0;
            bool sprawdzac = true;
            switch (cbRodzaj.SelectedItem.ToString())
            {
                case "Broń":
                    if (cbWojo.Checked == true & cbNinja.Checked == false & cbSura.Checked == false & cbSzaman.Checked == false)
                    {
                        foreach (var Obj in itemy.bron.wojo)
                        {
                            sprawdzac = true;
                            foreach (var Obj1 in itemy.bron.ninja)
                            {
                                if (Obj1 == Obj)
                                {
                                    sprawdzac = false;
                                    break;
                                }
                            }
                            if (sprawdzac)
                            {
                                foreach (var Obj2 in itemy.bron.sura)
                                {
                                    if (Obj2 == Obj)
                                    {
                                        sprawdzac = false;
                                        break;
                                    }
                                }
                            }
                            if (sprawdzac)
                            {
                                foreach (var Obj3 in itemy.bron.szaman)
                                {
                                    if (Obj3 == Obj)
                                    {
                                        sprawdzac = false;
                                        break;
                                    }
                                }
                            }
                            if (sprawdzac)
                            {
                                items[liczba] = Obj;
                                liczba++;
                            }

                        }
                        for (int i = 0; i < liczba; i++)
                        {
                            cbPrzedmiot.Items.Add(items[i]);
                        }
                    }
                    break;
                default:
                    break;
            }
        } 

Niestety aby przewidzieć wszystkie kombinacje tych 4 CheckBoxów musiałbym napisać 16 podobnych :( Cały czas staram się to rozwiązać, ale bez rezultatu. Dlatego też proszę o pomoc w tej sprawie i...

...w kolejnej, najważniejszej. O ile w pierwszym przypadku kilka dni logicznego myślenia i problem w końcu rozwiążę, o tylke w 2. przypadku tak łatwo nie będzie :( Otóż w cbRodzaj mam 14 itemów (m.in. "Broń") i tworzenie dla każdego Case oddzielnych kilkunastu linijek kody mija się z celem. Chciałem zrobić coś w stylu:

 void zaladuj()
        {
            object[] items = new object[9000];
            int liczba = 0;
            bool sprawdzac = true;
            switch (cbRodzaj.SelectedItem.ToString())
            {
                case "Broń":
			klasy(itemy.bron);
			break;
                default:
                    	break;
	    }

        void klasy(class klasa)
        {
[...]
        }

Jednak z wiadomych dla was przyczyn jest to niemożliwe :( Więc: Czy da się przekazać klasę jako parametr? Jakieś wskaźniki czy coś? Albo może macie lepszy pomysł jak to rozwiązać?
PROSZĘ O POMOC!

0

Tak, można przekazywać klasę (obiekt) jako parametr

 
public class JakasKlasa
{
public void Show()
{
Console.WriteLine("JakasKlasa");
}
}

gdzieś w kodzie np. main

 
static void ShowClass(JakasKlasa klasa)
{
klasa.Show();
}

W zasadzie niczym to się nie różni od przekazywania np. int, string, a w Twoim przypadku na pewno chciałbyś przekazać object :P

To tyle, do reszty pytania, nie mam po prostu siły -.- Poczytaj na początku o typach zmiennych :/

0

Eh... Nic nowego nie wprowadziłeś do tematu, bo nie wiem czy zauważyłeś, ale u mnie jest nie tylko JakasKlasa, ale również JakasInnaKlasa i JakasZupelnieInnaKlasa. Więc jak mam TO zrobić??

0

Ciężko to wszystko zrozumieć...

Ad 1) Ty chcesz znaleźć część wspólną zbiorów, np. a1 i a2 i umieścić ją w ComboBoxie?

Ad 2) Czy nie wystarczy Ci zrobienie oddzielnej metody dla każdego z 14 typów? (Nie wiem, co tam ma się w tym switchu dziać.)

0

Check Boxy oznaczają zbiory: a1, a2, a3 i a4. Chcę uzyskać :
(Z U W)/(X ∩ Y)
Gdzie:
Z, W - zbiory, które nie są zaznaczone w Check Boxach
X, Y - Zbiory, które są zaznaczone w Check Boxach

A w tym Switchu ma dziać się właśnie to. W tym kodzie jest rozpatrzony tylko 1-szy przypadek. Jeśli chciałbym rozpatrzyć kolejne 15 (czyli kolejnych kilkanaście linijek kodu) dla 20 klas, wyjdzie ok. 13800 linijek kodu :O Dlatego chciałbym napisać funkcję, załóżmy, w której rozpatrzę te 16 przypadków, tylko skąd ta funkcja ma wiedzieć z której klasy ma oddzielać... MAM! Jako parametr nie przekażę funkcji, tylko tabelę objektów!!! Coś jak to:

void zaladuj()
        {
            object[] items = new object[9000];
            int liczba = 0;
            bool sprawdzac = true;
            switch (cbRodzaj.SelectedItem.ToString())
            {
                case "Broń":
                    klasy(itemy.bron.wojo, itemy.bron.ninja, itemy.bron.sura, itemy.bron.szaman);
                    break;
                default:
                    break;
            }
        }

        void klasy(object[] a, object[] b, object[] c, object[] d)
        {
            // Kod rozpatrujący te 16 przypadków dla check boxów
        } 

ŚWIETNIE! Dzięki za pomoc :) Aha... tylko są może jeszcze jakieś funkcje w C# do pracy na zbiorach?

0

linq może być tu pomocne
http://msdn.microsoft.com/en-us/vcsharp/aa336746
zobacz część Set Operators
(Z U W) - to Union
(X ∩ Y) - to Intersect
() \ () - to Except

0

Ok problem rozwiązany! Dziękuję za pomoc, zwłaszcza panu massther :)

Kod rozwiązujący problem:

void zaladuj()
        {
            switch (cbRodzaj.SelectedItem.ToString())
            {
                case "Broń":
                    klasy(itemy.bron.wojo, itemy.bron.ninja, itemy.bron.sura, itemy.bron.szaman);
                    break;
                default:
                    break;
            }
        }

        void klasy(object[] a, object[] b, object[] c, object[] d)
        {
            ArrayList items = new ArrayList();
            ArrayList itemstemp = new ArrayList();
            ArrayList suma = new ArrayList();
            ArrayList roznica = new ArrayList();
            ArrayList sumatemp = new ArrayList();
            ArrayList roznicatemp = new ArrayList();
            object[,] cb = new object[4, 2];
            cb[0, 0] = cbWojo;
            cb[1, 0] = cbNinja;
            cb[2, 0] = cbSura;
            cb[3, 0] = cbSzaman;
            cb[0, 1] = a;
            cb[1, 1] = b;
            cb[2, 1] = c;
            cb[3, 1] = d;

            if (cbWszystkie.Checked)
            {
                itemstemp = new ArrayList(a.Union(b).Union(c).Union(d).ToArray());
                items = itemstemp;
            }
            for (int i = 0; i <= 3; i++)
            {
                if (((CheckBox)cb[i, 0]).Checked)
                {
                    if (roznica.Count == 0)
                    {
                        roznicatemp = new ArrayList(((object[])(cb[i, 1])));
                    }
                    else
                    {
                        roznicatemp = new ArrayList(roznica.ToArray().Intersect(((object[])(cb[i, 1]))).ToList());
                    }
                    roznica = roznicatemp;
                }
                else
                {
                    sumatemp = new ArrayList(suma.ToArray().Union(((object[])(cb[i, 1]))).ToList());
                    suma = sumatemp;
                }
            }


            
            bool prawda;
            foreach (var item in roznica)
            {
                prawda = true;
                foreach (var item2 in suma)
                {
                    if (item == item2)
                    {
                        prawda = false;
                        break;
                    }
                }
                if (prawda)
                {
                    items.Add(item);
                }
            }
            cbPrzedmiot.Items.Clear();
            cbPrzedmiot.Items.AddRange(items.ToArray());
              
0

Tym ciągłym pakowaniem i rozpakowywaniem chyba sobie tylko utrudniasz. Dlaczego trzymasz stringi jako object? Czemu używasz ArrayList, zamiast List<T>? Trzymanie w jednej tablicy CheckBoxów i stringów też może namieszać. Tego ostatniego foreacha chyba też byś mógł przy użyciu LINQ zrobić.

0
somekind napisał(a)

Tym ciągłym pakowaniem i rozpakowywaniem chyba sobie tylko utrudniasz. Dlaczego trzymasz stringi jako object? Czemu używasz ArrayList, zamiast List<T>? Trzymanie w jednej tablicy CheckBoxów i stringów też może namieszać. Tego ostatniego foreacha chyba też byś mógł przy użyciu LINQ zrobić.

Czemu? NIE MAM POJĘCIA! Tak jak napisałem w 1. poście, jestem dopiero początkującym i nie wiem np. co to jest List<T>. Czy twój sposób jest szybszy, lepszy, jakkolwiek polepsza jakość pracy?

PS. Co do ostatniego Foreach, tak, mógłbym zrobić to z Linq, ale napisałem to jeszcze wczoraj i póki co spełnia swoją rolę.

0

Jeśli jesteś początkujący, to pisanie tak zaawansowanych programów niczego Cię nie nauczy.
Tak, jest to wydajniejsze, ale przede wszystkim używanie listy generycznej List<T> (gdzie T to może być nazwa dowolnego typu, np. w Twoim przypadku string czy ComboBox) jest lepsze, bo zapewnia bezpieczeństwo typów. Nie możesz tam wrzucić obiektów różnych typów jak do ArrayList czy object[]. Wiesz jakie metody posiadają elementy takiej listy, więc np. zamiast ((CheckBox)cb[i, 0]).Checked możesz po prostu napisać cb[i].Checked. Kod jest dzięki temu czytelniejszy i ładniejszy. Podobnie nie wrzuca się do jednej kolekcji obiektów różnych klas, więc cb to powinny być u Ciebie dwie oddzielne listy albo np. jakieś Dictionary<ComboBox, string[]>.
No i jeszcze taka uwaga - w C# nie trzeba deklarować wszystkich zmiennych na początku metody. :)

0

Mam dziwne wrażenie, że ten problem nie jest taki skomplikowany, jak kod, który napisałeś.

Po pierwsze, klasy opisujące przedmioty warto byłoby zdefiniować tak, aby miały klasę bazową. Dzięki temu każda klasa będzie opisywała jakiś tam zbiór przedmiotów, ale algorytm dla nich wszystkich będzie jeden. Moja propozycja:

public abstract class Dane
{
    public string[] A1 { get; protected set; }
    public string[] A2 { get; protected set; }
    public string[] A3 { get; protected set; }
    public string[] A4 { get; protected set; }
}

public class A : Dane
{
    public A()
    {
        this.A1 = new string[] { "1", "2", "3", "4", "5", "6" };
        this.A2 = new string[] { "3", "4", "5", "8", "12", "32", "128" };
        this.A3 = new string[] { "9", "10", "11", "12", "13", "14" };
        this.A4 = new string[] { "13", "14", "15", "16", "17" };
    }
}

public class B : Dane
{
    public B()
    {
        this.A1 = new string[] { "q", "w", "e", "r", "t", "y" };
        this.A2 = new string[] { "q", "w", "e", "r", "x", "y", "z" };
        this.A3 = new string[] { "a", "s", "d", "f", "g", "h" };
        this.A4 = new string[] { "g", "h", "j", "k", "l" };
    }
}

Dalszą pracę można podzielić sobie na pojedyncze etapy:

  1. Określenie co wybrał użytkownik (które ComboBoxy) i którą klasę (A, B, itd.) w cbRodzaj.
  2. Wrzucenie tych danych do metody, która przeprowadzi obliczenia. (O ile dobrze rozumiem zdanie, to trzeba od iloczynu zaznaczonych elementów odjąć sumę niezaznaczonych.)
  3. Użycie tego do wypełnienia cbPrzedmioty.
    No i to by było na tyle. ;)

Ad 1) Zrobimy sobie prostą klasę do opisu wyboru użytkownika:

private class Konfig
{
    public bool Wybrane { get; private set; }
    public string[] Elementy { get; private set; }

    public Konfig(bool wybrane, string[] elementy)
    {
        this.Wybrane = wybrane;
        this.Elementy = elementy;
    }
}

I metodę, która przetworzy wybór z GUI oraz z klas dziedziczących po Dane w List<Konfig>:

private List<Konfig> PrzygotujDane(string rodzaj, bool wojo, bool ninja, bool sura, bool szaman)
{
    Dane d = null;

    switch (rodzaj)
    {
        case "A":
            d = new A();
            break;
        case "B":
            d = new B();
            break;
    }

    List<Konfig> dane = new List<Konfig>();
    dane.Add(new Konfig(wojo, d.A1));
    dane.Add(new Konfig(ninja, d.A2));
    dane.Add(new Konfig(sura, d.A3));
    dane.Add(new Konfig(szaman, d.A4));

    return dane;
}

Jak widać najpierw określamy która konkretnie klasa dostarczy nam list przedmiotów, a potem tworzymy z tego coś a la słownik łączący wartość z ComobBoxa z odpowiednią listą przedmiotów.

Ad 2) A to metoda wyznaczająca przemioty:

private IEnumerable<string> ObliczPrzedmioty(List<Konfig> dane)
{
    List<string> niezaznaczone = new List<string>();
    List<string> zaznaczone = new List<string>();
            
    foreach (var item in dane)
    {
        if (item.Wybrane)
        {
            if (zaznaczone.Count == 0)
            {
                zaznaczone.AddRange(item.Elementy);
            }
            else
            {
                zaznaczone = zaznaczone.Intersect(item.Elementy).ToList();    // iloczyn
            }
        }
        else
        {
            if (niezaznaczone.Count == 0)
            {
                niezaznaczone.AddRange(item.Elementy);
            }
            else
            {
                niezaznaczone = niezaznaczone.Union(item.Elementy).ToList();  // suma
            }
        }
    }

    IEnumerable<string> wynik = zaznaczone.Except(niezaznaczone);   // różnica
    return wynik;
}

W pętli, jeśli dane przedmioty są wybrane, to obliczamy ich iloczyn z innymi wybranymi, a jeśli niewybrane, to dodajemy je do pozostałych niewybranych. Na końcu obliczamy różnicę i wsio. Czytelne i łatwe w modyfikacji.

  1. Do jakiegoś Buttona czy gdzieś podpinamy taką metodę:
private void button1_Click(object sender, EventArgs e)
{
    List<Konfig> dane = this.PrzygotujDane(this.cbRodzaj.SelectedItem.ToString(),
        this.chkWojo.Checked, this.chkNinja.Checked, this.chkSura.Checked, this.chkSzaman.Checked);

    string[] przedmioty = this.ObliczPrzedmioty(dane).ToArray();

    this.cbPrzedmiot.Items.Clear();
    this.cbPrzedmiot.Items.AddRange(przedmioty);
}
0

Hehe wiem że nie trzeba deklarować zmiennych na początku funkcji, ale tak jestem nauczony i tak jest (jak dla mnie) przejrzyściej.

A co do twojego kodu, może i jest przejrzysty, ale nie dla mnie - początkującego. Niby rozumiem co tam jest, ale to dla mnie za bardzo skomplikowane :)

0

Kiedy właśnie to Twój kod jest skomplikowany i całkowicie nieczytelny, w ogóle nie wiadomo co tam się dzieje.
Zmień swój kod tak, żeby zamiast (X ∩ Y) \ (Z U W) zwracał (Z ∩ W) \ (X U Y). Będzie to łatwe?
W moim kodzie można dodać kolejny 1000 klas opisujący przedmioty i algorytm będzie działał dalej bez żadnych zmian. A u Ciebie?

1 użytkowników online, w tym zalogowanych: 0, gości: 1