Klasa bazowa z jedną lub większą ilością klas generycznych

0

Cześć, nie wiem za bardzo jak zadać pytanie, więc po prostu opiszę.
Czy w C# można zrobić coś takiego:

Mam klasę bazową:

public class Base<T>
{
  public virtual void AddItem(T item)
  {

  }
}

I teraz mogę sobie tak tworzyć klasy pochodne:

public class Derived: Base<string>
{
  public override void AddItem(string item)
  {

  }
}

I teraz pytanie jest takie, czy mogę w jakiś sposób zrobić, żeby klasa bazowa przyjmowała 1, lub 2, lub 3..., lub n klas generycznych? Metoda AddItem miałaby wtedy n przeciążeń. Da się?

0

Nie można, nie bardzo też rozumiem sens takiego działania. Zależnie od potrzeb można stworzyć wiele klas pochodnych dziedziczących z klasy bazowej. Nie rozumiem też sensu wykorzystywania wielu różnych typów generycznych w ramach jednej klasy. Logicznie ujmując zaprowadzi do stworzenia jakiejś super klasy do przechowywania wszystkiego. Jeżeli chcemy w klasie np. dodawać do jakiegoś kontenera obiekty różnego typu, można wydzielić im wspólny interfejs np.

public class Derived: Base<ISomething>
{
  public override void AddItem(ISomething item)
  {
 
  }
 
1

O ile dobrze zrozumiałem to chcesz coś w tym stylu?:

interface IAdd<in T>
{
    void AddItem(T item);
}

public abstract class Base: IAdd<string>, IAdd<int>
{
    public virtual void AddItem(string item)
    {
        throw new System.NotImplementedException();
    }

    public virtual void AddItem(int item)
    {
        throw new System.NotImplementedException();
    }
}

public class Derived : Base
{
    public override void AddItem(string item)
    {
        base.AddItem(item);
    }

    public override void AddItem(int item)
    {
        base.AddItem(item);
    }
}

A jak to za mało to zawsze można generować klasy w runtime. ;)

0
Juhas napisał(a):

I teraz pytanie jest takie, czy mogę w jakiś sposób zrobić, żeby klasa bazowa przyjmowała 1, lub 2, lub 3..., lub n klas generycznych? Metoda AddItem miałaby wtedy n przeciążeń. Da się?

Ale po co? Przecież metoda AddItem z klasy bazowej przyjmuje argument typ generycznego, który jej przekazano. Czemu chcesz overridować AddItem(string) na AddItem(string)?

0
mariano901229 napisał(a):

Nie można, nie bardzo też rozumiem sens takiego działania. Zależnie od potrzeb można stworzyć wiele klas pochodnych dziedziczących z klasy bazowej. Nie rozumiem też sensu wykorzystywania wielu różnych typów generycznych w ramach jednej klasy. Logicznie ujmując zaprowadzi do stworzenia jakiejś super klasy do przechowywania wszystkiego. Jeżeli chcemy w klasie np. dodawać do jakiegoś kontenera obiekty różnego typu, można wydzielić im wspólny interfejs np.

Chciałem sobie trochę ułatwić sprawę. Przykład z życia. Mam sobie klasę bazową:

public class Base
{
  public virtual void AddItem(Base item)
  {

  }

}

Po klasie Base dziedziczy mi kilka innych klas. I teraz któraś z tych klas może trzymać inne elementy, np:

 
public class Parent: Base
{
  List<DerivedFromBase> m_items;

  public override void AddItem(Base item)
  {
  }
}

W tym momencie, żeby dodać item do listy, muszę zrobić rzutowanie. Ale najpierw muszę się upewnić, czy item jest odpowiedniego typu i wychodzi mi coś takiego:

 
public override void AddItem(Base item)
{
  Debug.Assert(item is DerivedFromBase); //albo jakiś if ... throw albo try..catch przy rzutowaniu
  m_items.Add((DerivedFromBase)item);
}

Oczywiście, mógłbym mieć od razu listę zdefiniowaną jako List<Base>, ale ja tu oczekuję obiektu konkretnego typu.

Teraz mam też obiekty, które mogą trzymać kilka rodzajów podobiektów, np:

public class MultiParent: Base
{
  List<DerivedFromBase> m_dfbItems;
  List<OtherDerivedFromBase> m_odfbItems;

  public override void AddItem(DerivedFromBase item)
  {

  }

  public override void AddItem(OtherDerivedFromBase item)
  {

  }
}

Chciałem to zrobić dziedzicząc po prostu po klasie bazowej (gdyż klasa bazowa robi coś jeszcze w swojej implementacji), ale widzę, że się tak nie da. Odpowiedź DibbyDum zaakceptowałem, bo gdyby nie to, że klasa bazowa robi coś jeszcze w swojej implementacji, to użycie interfejsów w taki sposób byłoby właśnie tym, czego szukałem :)

0

Jeżeli robisz rzutowanie przed dodaniem do listy, to na 90% coś robisz źle. Ogólnie wygląda na to, że masz jakiś bałagan w hierarchi albo po prostu nie potrzebujesz typów generycznych, skoro potrzebujesz konkretnie typowanych metod i oddzielnych list.

0

Mam taką hierarchię. Jest klasa bazowa, z której dziedziczą wszystkie inne klasy.
Każdy taki item posiada swój unikalny handle. Podczas tworzenia itemu, klasa bazowa odpowiednio nadaje sobie handle. I tylko ona to może zrobić (utworzyć handle) - na dole opisuję, jak to robię.

Klasa bazowa udostępnia też metodę AddChild, bo:

  1. Każdy item W TEORII może być rodzicem dla każdego itema
  2. Każdy item (w klasie bazowej) ma parentHandle (bo teoretycznie może być dzieckiem każdego itema), który jest ustawiany na poziomie klasy bazowej właśnie w metodzie AddChild.

Ale dzieci itemów już są konkretne.
Wygląda to tak:

//klasa bazowa
public abstract class Item
{
  Handle m_handle
  Handle m_parentHandle;

  public Item()
  {
    m_handle = new Handle(); //w uproszczeniu
  }

  public virtual void AddItem(Item item)
  {
    item.m_parentHandle = m_handle;
  }
}

//jakiś tam item z różnymi polami
public class BigItem: Item
{
  public int Limit { get; set; }
}

//i item, który faktycznie jest rodzicem
public class ParentItem: Item
{
  List<BigItem> m_items = new List<BigItem>();

  public override void AddItem(Item item)
  {
    base.AddItem(item);
    m_items.Add((BigItem)item);
  }

  public void Foo()
  {
    foreach(Item item in m_items)
      item.Limit = 0;
  }
}

Mniej więcej tak to wygląda. W pierwszej wersji metoda AddItem była w osobnym interfejsie. Ale problemem okazał się parentHandle. ParentHandle ma być przypisany zawsze. Jeśli ktoś tego nie zrobi w AddItem, wtedy dupa zbita. Jak by to można było rozwiązać inaczej?

Jeszcze pokrótce opiszę klasę Handle. Wygląda to tak, że jest sobie klasa wewnątrz klasy bazowej Item:

 
public abstract class Item
{
  public class _Handle
  {
    //jakieś tam pola

    protected internal _Handle() {} //jakieś tam działania
   
  }
}

//następnie, żeby można było używać tej klasy jakby poza Itemem, jest zrobiona klasa dziedzicząca:
public class Handle: Item._Handle
{
//tu nie ma niczego.
}

Dzięki takiemu rozwiązaniu Handle jest ściśle związany z klasą Item. Istnieje tylko dla niej. I tylko klasa bazowa Item może utworzyć takiego handla.

0

Możesz zastosować Template method pattern, to jest najpierw napisałem ten kod a potem sobie przypomniałem że taki wzorzec istnieje dlatego podspodem w takiej pokracznej wersji :P.
Lepsza wersja

class Base
{
   public void AddItem(Base Item)
   { 
       //handle
       AddItemTemple(item);
   }
   protected virtual void AddItemTemple(Base item)
   {
     //
   }
}
class SecondClass :base
{
   List<SecondClass > list;
   protected ovveride AddItemTemple(Base item)
   {
          list.Add((SecondClass)item);
   }
}
class Base 
{
 protected Dictionary<Type, Action<Base>> addMethdos;
 public void AddItem(Base item)
 {
     // hadle ect...
     MagicAdd(addMethdos,item);
 }
// moze byc stactic
 protected void MagicAdd(Dictionary<Type, Action<Base>> methdos, Base toAdd>)
{
     var type = toAdd.GetType();
    if( methdos.ContainsKey(type ))
    {
           methdos[type].(toAdd);
    }
}
class SecondClass :base
{
   // ustawiasz te metody 
}

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