2016-10-02 333 views
1

我的大腦會爆炸。 :)所以我想從你那裏得到幫助。 請考慮一下我的問題,例如關於程序員謎題。 (其實,也許這對你來說是很容易的問題,但不適合我)。繼承泛型類C#

需要創建對象數組。例如列表,其中T是類。 (我將在下面描述T類)。此外,它需要創建「容器」,將包含此數組和一些方法來處理這個數組。例如Add(),Remove(int IndexToRemove)。 類T必須有字段「容器」,這樣我們數組中的每個元素都能夠知道它包含的內容並訪問其容器的字段和方法。注意,在這種情況下,類T應該有類型參數。事實上,事先並不知道使用哪種容器的類型。 讓我們將這個類容器表示爲A,將類元素(類T)表示爲AUnit。

代碼:

class Program 
{ 
    static void Main(string[] args) 
    { 
     A a = new A(); 
     a.Add(); 
     a.Units[0].SomeField +=100;  
     Console.ReadKey(); 
    } 
} 


class A 
{ 
    public List<AUnit> Units; 

    public A()//ctor 
    { 
     Units = new List<AUnit>(); 
    } 
    public void Add() 
    { 
     this.Units.Add(new AUnit(this)); 
    } 
} 
class AUnit 
{ 
    public int SomeField; 
    public A Container; 
    public string Name { get; private set; } 
    public AUnit(A container) 
    { 
     this.SomeField = 43; 
     this.Container = container; 
     this.Name = "Default"; 
    } 
} 

公共領域應受到保護或私人的課程,但後來我們想想這一點。 您可以問「爲什麼我們在AUnit中創建公共A容器字段」?我們創建字段公共字符串名稱{獲取;私人設置;}(實際上屬性,但從來沒有)。我們還希望能夠更改此字段的值,例如方法[Class AUnit] public bool Rename(string newName)();.這種方法的主要思想是隻有在數組(公共列表單位;)中沒有一個元素具有相同名稱(如newName)的情況下才更改名稱字段。但要實現這一點,Rename方法必須能夠訪問當前使用的所有名稱。這就是爲什麼我們需要Container領域。

的擴展版本代碼AUnit

class AUnit 
{ 
    public int SomeField; 
    public A Container; 
    public string Name { get; private set; } 
    public AUnit(A container) 
    { 
     this.SomeField = 43; 
     this.Container = container; 
     this.Name = "Default"; 
    } 
    public bool Rename(String newName) 
    { 
     Boolean res = true; 
     foreach (AUnit unt in this.Container.Units) 
     { 
      if (unt.Name == newName) 
      { 
       res = false; 
       break; 
      } 
     } 
     if (res) this.Name = String.Copy(newName); 
     return res; 
    } 
} 

確定。如果你仍然閱讀它,讓我們繼續。現在我們需要創建類B和類BUnit,它們與A類和Aunit類非常相似。最後,這個難題的主要問題是我們如何做到這一點?當然,我可以CopyPaste和位修改A和AUnit並創建此代碼。

class B 
{ 
    public List<BUnit> Units; //Only Type Changing 

    public B()//ctor Name changing... 
    { 
     Units = new List<BUnit>();//Only Type Changing 
    } 
    public void Add() 
    { 
     this.Units.Add(new BUnit(this));//Only Type Changing 
    } 
} 
class BUnit 
{ 
    public int SomeField; 
    public B Container;//Only Type Changing 
    public string Name { get; private set; } 
    public A a; //NEW FIELD IS ADDED (just one) 

    public BUnit(B container) //Ctor Name and arguments type changing 
    { 
     this.SomeField = 43; 
     this.Container = container; 
     this.Name = "Default"; 

     this.a=new A(); //New ROW (just one) 
    } 
    public bool Rename(String newName) 
    { 
     Boolean res = true; 
     foreach (BUnit unt in this.Container.Units) //Only Type Changing 
     { 
      if (unt.Name == newName) 
      { 
       res = false; 
       break; 
      } 
     } 
     if (res) this.Name = String.Copy(newName); 
     return res; 
    } 
} 

我可以通過這種方式使用這個類。

static void Main(string[] args) 
{ 
    B b = new B(); 
    b.Add(); 
    b.Units[0].a.Add(); 
    b.Units[0].a.Units[0].SomeField += 100; 
    bool res= b.Units[0].a.Units[0].Rename("1"); 
    res = b.Units[0].a.Units[0].Rename("1"); 

    Console.ReadKey(); 
} 

這種結構可以用來創建「非均質樹」。

幫助,我需要幫助,只是沒有人...。 [The Beatles]

我使用CopyPaste創建了B和BUnit。 但是,如何使用「宏定義」或「通用」,繼承或其他優雅風格的東西呢? (C#語言) 我認爲沒有理由來描述我所有不成功的嘗試和子問題。已經有話題太長了。 :)

非常感謝,如果您仍然閱讀並理解我想問的問題。

回答

3

您需要實現一個基本類型,可以稱其爲UnitBase,具有所有常見功能。我想構建你的代碼的方式如下:

  1. 您的容器創建一個接口,這樣你可以改變實現更高性能的解決方案,而無需修改你將被添加到容器中的元素。

    public interface IContainer 
    { 
        Q Add<Q>() where Q : UnitBase, new(); 
        IEnumerable<UnitBase> Units { get; } 
    } 
    
  2. 按照1中說明的思路,爲什麼不讓搜索邏輯屬於容器?它使更多的意義,因爲這將主要取決於該容器是如何實現的:

    public interface IContainer 
    { 
        Q Add<Q>() where Q : UnitBase, new(); 
        IEnumerable<UnitBase> Units { get; } 
        bool Contains(string name); 
    } 
    

    的​​具體實現可以是以下幾點:

    public class Container : IContainer 
    { 
        public Container() 
        { 
         list = new List<UnitBase>(); 
        } 
    
        private List<UnitBase> list; 
    
        public Q Add<Q>() where Q: UnitBase, new() 
        { 
         var newItem = Activator.CreateInstance<Q>(); 
         newItem.SetContainer(this); 
         list.Add(newItem); 
         return newItem; 
        } 
    
        public IEnumerable<UnitBase> Units => list.Select(i => i); 
        public bool Contains(string name) => 
         Units.Any(unit => unit.Name == name); 
    } 
    
  3. AUnit創建一個基類和BUnit類型的所有常見功能:

    public abstract class UnitBase 
    { 
        protected UnitBase() 
        { 
        } 
    
        public IContainer Container { get; private set; } 
        public int SomeField; 
        public string Name { get; private set; } 
        public void SetContainer(IContainer container) 
        { 
         Container = container; 
        } 
    
        public bool Rename(String newName) 
        { 
         if (Container.Contains(newName)) 
          return false; 
    
         this.Name = newName; //No need to use String.Copy 
         return true; 
        } 
    } 
    
  4. 執行您[R具體類型:

    public class BUnit : UnitBase 
    { 
        public int SpecificBProperty { get; private set; } 
        public BUnit() 
        { 
        } 
    } 
    

這種方法的缺點?那麼容器的類型必須是<UnitBase>,我已經刪除了泛型,因爲它在這種特殊情況下確實沒有太大的作用,因爲它在泛型中是不變的。

另外,請記住,在任何類型的系統避免了以下內容:

myContainer.Add<BUnit>(); 
myContainer.Add<AUnit>(); 

如果在同一容器中有兩種不同類型的不是一個選項,然後這一整套種了崩潰了。這個問題也出現在以前的解決方案中,所以它不是什麼新東西,我只是忘了指出來。

+0

雖然我試圖理解和測試你的代碼,你能告訴我說:我們能避免與構造函數,你描述的,如果我們將使用無參數construcor但增加功能SetContainer(有些PARAMS),以單位類,並使用它的問題在public void Add(T item){list.Add(item); item.SetContainer(一些參數)}。我們也可以在Add()函數內創建項目並使用此函數而不使用參數? – JustOneQuestion

+0

@JustOneQuestion回答你的第一個問題:這個問題還是一樣。你想在基類「UnitBase」中擁有所有常見的功能。這個類並不知道是什麼類型可能從中獲得的,所以'Container'永遠是類型'的IContainer '這是不變的。 – InBetween

+0

@JustOneQuestion編輯廣泛的基於你的第二個問題,回答'的IContainer '刪除了通用的類型,因爲它沒有在您的特定場景下'T'只能是'UnitBase'做多。另外請注意'Add'現在返回添加的元素,以便您可以獲取對新創建項目的引用。此外,如果'AUnit','BUnit'等具有無參數構造函數以便能夠在'Add'內部創建一個構造函數,則該解決方案可以工作。 – InBetween

0

InBetween,我非常感謝您的建議。其實我不能說我完全理解了你的答案,但是用你的想法做了我想做的。

看起來像我的變種運作良好。不過,我想聽聽你的(和每個人)對下面描述的代碼的意見。這種結構的主要目標是創建非同質樹。所以你可以從這方面估計它。

首先。我們需要爲這兩個類創建接口。我們在那裏描述所有「交叉使用」功能。

public interface IUnit<T> 
{ 
    string Name { get;} 
    void SetContainer(T t); 
    bool Rename(String newName); 
} 
public interface IContainer 
{ 
    bool IsNameBusy(String newName); 
    int Count { get; } 
} 

下一頁。爲未來的繼承創建單元類的基礎。我們將在這個繼承者中使用來自Container Base的方法,所以我們需要泛型屬性和IUnit接口。

class UnitBase<T> : IUnit<T> where T : IContainer 

不幸的是,我不知道如何解決構造函數參數的問題。這就是爲什麼我使用方法

SetContainer(T container). 

代碼:UnitBase

class UnitBase<T> : IUnit<T> where T : IContainer 
{ 
    protected T Container; 
    public string Name { get; private set; } 
    public UnitBase() 
    { 
     this.Name = "Default"; 
    } 
    public void SetContainer(T container) 
    { 
     this.Container = container; 
    } 
    public bool Rename(String newName) 
    { 
     bool res = Container.IsNameBusy(newName); 
     if (!res) this.Name = String.Copy(newName); 
     return !res; 
    } 
} 

下一步。創建ContainerBase

ContainerBase應該:

1)具有的IContainer接口。

2)有什麼它將包含的信息:)

... where U : IUnit<C>, new() 

3 ....有大約本身是什麼樣的信息。我們需要將此信息作爲參數傳遞給SetContainer()方法。

代碼ContainerBase:

class ContainerBase<U, C> : IContainer //U - Unit Class. C-Container Class 
    where U : IUnit<C>, new() 
    where C : ContainerBase<U, C> 
{ 
    protected List<U> Units; 
    public U this[int index] { get { return Units[index]; } } 
    public ContainerBase()//ctor 
    { 
     this.Units = new List<U>(); 
    } 
    public void Add() 
    { 
     this.Units.Add(new U()); 
     this.Units.Last().SetContainer(((C)this));//may be a bit strange but actualy this will have the same type as <C> 
    } 
    public bool IsNameBusy(String newName) 
    { 
     bool res = false; 
     foreach (var unt in this.Units) 
     { 
      if (unt.Name == newName) 
      { 
       res = true; 
       break; 
      } 
     } 
     return res; 
    } 
    public int Count { get { return this.Units.Count; } } 
} 

角色((TContainer)(本))可以是有點奇怪。但是使用ContainerBase我們總是應該使用NewInheritorContainer。所以這個轉換是不需要做任何事情......貌似......

最後。這個類可以像這個例子一樣使用。

class SheetContainer : ContainerBase<SheetUnit,SheetContainer> {public SheetContainer(){}} 

class SheetUnit : UnitBase<SheetContainer> 
{ 
    public CellContainer Cells; 
    public PictureContainer Pictures; 
    public SheetUnit() 
    { 
     this.Cells = new CellContainer(); 
     this.Pictures = new PictureContainer(); 
    } 
} 

class CellContainer : ContainerBase<CellUnit, CellContainer> { public CellContainer() { } } 

class CellUnit : UnitBase<CellContainer> 
{ 
    public string ValuePr;//Private Field 
    private const string ValuePrDefault = "Default"; 
    public string Value//Property for Value 
    { 
     //All below are Just For Example. 
     get 
     { 
      return this.ValuePr; 
     } 
     set 
     { 
      if (String.IsNullOrEmpty(value)) 
      { 
       this.ValuePr = ValuePrDefault; 
      } 
      else 
      { 
       this.ValuePr = String.Copy(value); 
      } 
     } 
    } 
    public CellUnit() 
    { 
     this.ValuePr = ValuePrDefault; 
    } 
} 

class PictureContainer : ContainerBase<PictureUnit, PictureContainer> { public PictureContainer() { } } 

class PictureUnit : UnitBase<PictureContainer> 
{ 
    public int[,] Pixels{get;private set;} 
    public PictureUnit() 
    { 
     this.Pixels=new int[,]{{10,20,30},{11,12,13}}; 
    } 
    public int GetSizeX() 
    { 
     return this.Pixels.GetLength(1); 
    } 
    public int GetSizeY() 
    { 
     return this.Pixels.GetLength(0); 
    } 
    public bool LoadFromFile(string path) 
    { 
     return false; 
    } 
} 
static void Main(string[] args) 
    { 
     SheetContainer Sheets = new SheetContainer(); 
     Sheets.Add(); 
     Sheets.Add(); 
     Sheets.Add(); 
     Sheets[0].Pictures.Add(); 
     Sheets[1].Cells.Add(); 
     Sheets[2].Pictures.Add(); 
     Sheets[2].Cells.Add(); 


     Sheets[2].Cells[0].Value = "FirstTest"; 
     bool res= Sheets[0].Rename("First");//res=true 
     res=Sheets[2].Rename("First");//res =false 

     int res2 = Sheets.Count; 
     res2 = Sheets[2].Pictures[0].Pixels[1, 2];//13 
     res2 = Sheets[2].Pictures.Count;//1 
     res2 = Sheets[1].Pictures.Count;//0 
     res2 = Sheets[0].Pictures[0].GetSizeX();//3 
     Console.ReadKey(); 
    } 

看起來像是我想要的。但我沒有測試完整。 讓我說,再次感謝你,插圖中