2016-10-02 321 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(); 
    } 

看起来像是我想要的。但我没有测试完整。 让我说,再次感谢你,插图中