如何处理/强制转换递归泛型类继承?



我将绘制一个例子来说明我的幕后问题。

假设我有这个基本泛型类:

public abstract class ContainerBase<T>
{
Guid Id {get; init;}
IList<T> Items {get; set;}
bool IsLeaf {get; set;} = false;
/// omitted constructors and so
}

然后我有一大堆(未定义数量)具体的容器类,它们有另一个ContainerBase<...>作为 T 类型参数:

public class RootContainer : ContainerBase<ChildContainer1>
{...}
public class ChildContainer1: ContainerBase<ChildContainer2>
{...}
public class ChildContainer2: ContainerBase<ChildContainer3>
{...}
...
public class ChildContainerNminus1: ContainerBase<ChildContainerN>
{...}
public class ChildContainerN: ContainerBase<int> // the recursion end here.
{
...
IsLeaf = true;
}

现在假设我有一个来自 Utility 类的AddContainer方法,并且可以访问完全填充递归子容器的RootContainer对象(例如单例)。

public static class ContainerUtility
{
// What is the Type of the recursive currentContainer ?
public static ContainerBase<T> FindContainer<T>(Guid id, ContainerBase<?> currentContainer)
{
if(currentContainer.Id == id)
return currentContainer;
if(currentContainer.IsLeaf) return default;
foreach(var item in currentContainer.Items)
{
var potential = FindContainer(id, item);
if(potential != default) return potential;
}
return default;
}
public static bool AddContainer<T>(ContainerBase<T> container, Guid parentId)
{            
// potential should be of container's parent type (ContainerBase<"T-1">)
// but how to "bybass" an expected type parameter as I cannot know it ?
var potential = FindContainer<?>(parentId, RootContainer.Instance)
if(potential != default && potential is ContainerBase<?>) 
{
potential.Items.Add(container)
return true;
}
return false;
}
}

你看,我的问题是我有一个便于递归搜索的基本类型ContainerBase,因为所有子类都允许访问Items列表以进行递归。

但是在递归的每一步,它都是不同的实际ContainerBase<?>类型。 所以我不能对方法参数执行强制转换。

也许使用公开List<object> Items的顶级接口?不确定最终会好起来。

Bellow 是我关于我的问题的中间解决方案。 我会保留它作为记录,或者如果您要求它澄清此回复,我会删除它。

好的,我现在得到了更有趣的东西。我希望你对这个解决方案的批评,我最终得到:

主要是我用非通用接口抽象了一个更高的方法,以避免我在 OP 中描述的问题。

界面

public interface IContainer
{
int TAG { get; init; } // usefull for logging purpose
string Name { get; }
Guid Id { get; init; }

public bool IsLeaf { get;}
IList<IContainer>? GetContainers();
void SetContainers(List<IContainer> value);
}

基类

public abstract class ContainerBase<T> : IContainer where T : IContainer
{
public int TAG { get; init; }
public Guid Id { get; init; }
private IList<IContainer>? _containers = new List<IContainer>();
public IList<T> Items { get; set; } = new List<T>();
public bool IsLeaf => this.GetType() == typeof(T);
public string Name => this.GetType().Name + "_" + TAG;       
public ContainerBase(Guid id)
{
TAG = ContainerUtils.ContainerCount++;
Id=id;
}
public ContainerBase()
{
TAG = ContainerUtils.ContainerCount++;
Id = Guid.NewGuid();
}
public IList<IContainer>? GetContainers()
{
if(Items == null) return null;
if(_containers == null || !_containers.Any())
_containers = Items.Where(x => x!=null).Select(x => (IContainer)x!).ToList();
return _containers;
}
public void SetContainers(List<IContainer> value)
{
Items = new List<T>();
foreach(var item in value)
{
if (item is T)
Items.Add((T)item);
}            
}
}

具体类

internal class RootContainer : ContainerBase<Child1Container>
{
public RootContainer(Guid id) : base(id)
{
}
public RootContainer() : base()
{
}
}

中间容器是相同的,只是类名更改(在我的测试用例中 X = 1 到 3)

internal class ChildXContainer : ContainerBase<ChildX+1Container>
{
public ChildXContainer(Guid id) : base(id)
{
}
public ChildXContainer() : base()
{
}
}

叶类(我的链式容器类递归的终点)。

internal class LeafContainer : ContainerBase<LeafContainer>
{
public int IntItem { get; set; }

public LeafContainer() : base()
{            
}
public LeafContainer(Guid id) : base(id)
{
}
}

请注意,我正在使用一个技巧来检测ContainerBase<T>的具体实现是否是叶子: 如果这些类是叶子,那么它们必须从自身ContainerBase<>派生出来。

有点像 CRTP 语法,但没有它的含义。

所以我对这个技巧并不完全满意,但比我之前的尝试要好。

实用工具类

internal static class ContainerUtils
{
public static int ContainerCount = 0;
public static Guid IdToSearch { 
get
{
if(!AllIds.Any()) 
return Guid.Empty;
return AllIds[new Random().Next(AllIds.Count - 1)];
}
//set { IdToSearch = value; } 
}
public static List<Guid> AllIds { get; set; } = new();
private static RootContainer _root = BuildContainers();
public static RootContainer Root => _root;
private static RootContainer BuildContainers()
{
LeafContainer Leaf = new LeafContainer();
Child3Container Child3 = new Child3Container();
Child2Container Child2 = new Child2Container();
Child1Container Child1 = new Child1Container();
RootContainer Root = new RootContainer();                  
Root.Items.Add(Child1);
Child1.Items.Add(Child2);
Child2.Items.Add(Child3);
Child3.Items.Add(Leaf);
Leaf.IntItem = 12;
AllIds.Add(Root.Id);
AllIds.Add(Child1.Id);
AllIds.Add(Child2.Id);
AllIds.Add(Child3.Id);
AllIds.Add(Leaf.Id);
return Root;
}
private static IContainer? _GetSubContainer(this IContainer container, int index)
=> (container == null ||
container.GetContainers() == null ||
index >= container.GetContainers()!.Count) ? null : container.GetContainers()![index];
public static string ContainersToString()
=> ContainersToString(Root);
public static string ContainersToString(IContainer? fromContainer)
{
if (fromContainer == null) return string.Empty;
int i = 0;
string tab = "  ";
string res = "";
while(fromContainer != null)
{
res += tab.Repeat(i) + "+" + fromContainer.Name??"NULL";
res += "n";
i++;
fromContainer = _GetSubContainer(fromContainer, 0);
}
return res;
}       
public static IContainer? SearchContainer(Guid id)
=> SearchContainer(id, Root);
public static IContainer? SearchContainer(Guid id, IContainer? fromContainer)
{
if (fromContainer == null) return null;
if (fromContainer.Id == id)
return fromContainer;
if (fromContainer.IsLeaf)
return null;
return SearchContainer(id, fromContainer._GetSubContainer(0));
}
public static bool SetItemToContainer(Guid id, IContainer newContainer) 
{
var container = SearchContainer(id);
if(container == null) return false;
if (container._GetSubContainer(0) == null || (container.GetContainers()![0].GetType() != newContainer.GetType()))
return false;
container.GetContainers()![0] = newContainer;
return true;
}
}

程序及其输出

Console.WriteLine(ContainerUtils.ContainersToString());
IContainer newChild2 = new Child2Container();
Console.WriteLine("Child2's Name : " + ContainerUtils.SearchContainer(ContainerUtils.AllIds[2])?.Name ?? "NULL");
Console.WriteLine("New Child2's Name : " + newChild2.Name);
ContainerUtils.SetItemToContainer(ContainerUtils.AllIds[1], newChild2);
Console.WriteLine(ContainerUtils.ContainersToString());

输出

+RootContainer_4
+Child1Container_3
+Child2Container_2
+Child3Container_1
+LeafContainer_0

Child2's Name : Child2Container_2
New Child2's Name : Child2Container_5
+RootContainer_4
+Child1Container_3
+Child2Container_5
<小时 />

旧答案

我创建了一个新的测试项目,其中包含 OP 的更简单版本。 这是我的结论,是的,发现使用界面作为"工作"解决方案(我不完全满意)。 请让我知道你的想法。

没有泛型参数的接口:

public interface IContainer
{
string Name => this.GetType().Name;
public Guid Id { get; init; }
public IContainer? Item { get; set; }
public bool IsLeaf => Id == Guid.Empty;
}

带有泛型参数的基本抽象类:

public abstract class ContainerBase<T> : IContainer where T : IContainer
{
public Guid Id { get; init; }
public T? Item { get; set; }
IContainer? IContainer.Item { get => Item; set => Item = (T)value; }
public ContainerBase(Guid id)
{
Id=id;
}
public ContainerBase()
{
Id = Guid.NewGuid();
}
}

起始混凝土Container

internal class RootContainer : ContainerBase<Child1Container>
{
public RootContainer(Guid id) : base(id)
{
}
public RootContainer() : base()
{
}
}

孩子Container具体的课程。在我的项目中,有Child1Container,Child2Container和Child3Container。我只在这里显示Child1Container。除类名外,其他相同。

internal class Child1Container : ContainerBase<Child2Container>
{
public Child1Container(Guid id) : base(id)
{
}
public Child1Container() : base()
{
}
}

结尾Container(在这里注意Leaf):这是我发现代码最丑陋的地方。

internal class LeafContainer : IContainer
{
public int IntItem { get; set; }
public Guid Id { get; init ; }
/// Meh, would be nice to avoid this.
public IContainer? Item { get => null; set => Item = null; }
public LeafContainer()
{
Id = Guid.Empty;
}
}

我的实用类:

internal static class ContainerUtils
{
public static Guid IdToSearch { get; set; }
private static RootContainer _root = BuildContainers();
public static RootContainer Root => _root;
private static RootContainer BuildContainers()
{
LeafContainer Leaf = new LeafContainer();
Child3Container Child3 = new Child3Container();
Child2Container Child2 = new Child2Container();
Child1Container Child1 = new Child1Container();
RootContainer Root = new RootContainer();
Root.Item = Child1;
Child1.Item = Child2;
Child2.Item = Child3;
Child3.Item = Leaf;
Leaf.IntItem = 12;
IdToSearch = Root.Id;
return Root;
}
public static IContainer? SearchContainer(Guid id)
=> SearchContainer(id, Root);
public static IContainer? SearchContainer(Guid id, IContainer? fromContainer)
{
if (fromContainer == null) return null;
if(fromContainer.Id == id)
return fromContainer;
if(fromContainer.IsLeaf)
return null;
return SearchContainer(id, fromContainer.Item);
}
}

最后我的Program

using TestRecursiveGenerics;
var res = ContainerUtils.SearchContainer(ContainerUtils.IdToSearch);
Console.WriteLine("Searching IContainer's Id, and we found : "+ res?.Name ?? "NULL");

最新更新