在泛型类C#

时间:2016-10-02 12:00:25

标签: c# generics inheritance

我的大脑会爆炸。 :)所以我想得到你的帮助。 请考虑我的问题,就像程序员之谜一样。 (实际上。也许这对你来说很容易,但不适合我。)

需要创建对象数组。例如,其中T是类的List。 (我将在下面描述T类)。还需要创建包含此数组的“容器”以及使用此数组的一些方法。例如Add(),Remove(int IndexToRemove)。 类T必须具有字段“Container”,这样我们数组的每个元素都能够知道它包含在哪里并且可以访问其容器的字段和方法。请注意,在这种情况下,类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中创建公共容器字段”?我们创建字段公共字符串名称{get; private set;}(实际上是属性但是从不明白)。而且我们希望能够更改此字段的值,例如方法[Class AUnit] public bool Rename(string newName)();.如果数组中没有一个元素(public List Units;)具有与newName相同的名称,则此方法的主要思想是仅更改Name字段。但要实现此目的,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();
}

这种结构可用于制造“非同质树”。

帮助,我需要别人的帮助,没有任何人...... [甲壳虫乐队]

我使用CopyPaste创建了B和BUnit。 但是如何使用“宏定义”或“通用”,继承或优雅风格的其他任何东西呢? (C#语言) 我认为没有理由描述我所有不成功的尝试和子问题。话题太长了。 :)

如果您仍然阅读它并了解我想问的内容,那么非常感谢。

2 个答案:

答案 0 :(得分: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);
    }
    

    IContainer的具体实现可能如下:

    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. 为您的AUnitBUnit类型创建基类,从而缩小所有常用功能:

    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. 实施具体类型:

    public class BUnit : UnitBase
    {
        public int SpecificBProperty { get; private set; }
        public BUnit()
        {
        }
    }
    
  5. 这种方法的缺点是什么?好吧,容器必须是<UnitBase>类型,我删除了泛型类型,因为它在这种特殊情况下确实没有做太多,因为它在泛型类型中是不变的。

    另外,请记住类型系统中没有任何内容可以避免以下情况:

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

    如果在同一容器中有两种不同的类型不是一种选择,那么整个设置就会崩溃。这个问题在以前的解决方案中也存在,所以它不是新的东西,我只是忘了指出它。

答案 1 :(得分: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

不幸的是我还不知道如何使用Constructor参数解决问题。这就是我使用方法

的原因
 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()方法。

Code 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; } }
}

Cast((TContainer)(this))可能有点奇怪。但是使用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();
    }

看起来像我想要的那样。但我没有测试它。 让我说再次感谢你, InBetween