【问题标题】:Interdependent Generic Classes?相互依赖的泛型类?
【发布时间】:2014-07-27 09:06:37
【问题描述】:

这篇文章的底部是一个解决方案的示例,尽管该示例显然是无效的,因为它在继承时使用了 BaseNode 和 BaseEdge 而没有提供类型。

我正在尝试创建一个抽象图类,其中节点类和边类也必须是抽象的。每个类的单独实现将添加方法和属性以及从基类实现抽象方法。

我有一个现有的解决方案,它只对所有内容使用基本类型,但是我很快发现自己陷入了混乱。

只有图类必须公开,尽管其他类可以公开,因此涉及节点和边类嵌套在图类中的解决方案是可以接受的。

有没有什么方法可以构造成所有属性都是正确的类型,而不需要在系统中的任何地方进行昂贵的强制转换?更重要的是实现类不必到处强制转换,但在这种情况下性能绝对是一个问题(实际上是),所以我宁愿完全避免强制转换。

abstract class Graph<TNode, TEdge>
    where TNode : BaseNode<TEdge>
    where TEdge : BaseEdge<TNode>
{
    TNode root;
    List<TNode> nodes;
    List<TEdge> edges;

    public abstract float process();
}

abstract class BaseNode<TEdge>
    // THIS HERE IS THE PROBLEM
    where TEdge : BaseEdge
{
    List<TEdge> inputs;

    public List<TEdge> Inputs
    {
        get
        {
            return inputs;
        }
    }

    public abstract float process();
}

abstract class BaseEdge<TNode>
    // THIS HERE IS THE PROBLEM
    where TNode : BaseNode
{
    TNode from;
    TNode to;

    public TNode To
    {
        get
        {
            return to;
        }
    }

    public TNode From
    {
        get
        {
            return from;
        }
    }

    public abstract float process();
}

@Marceln 想看看我现有的实现,所以就在这里。没有泛型也是一样的。

abstract class Graph
{
    BaseNode root;
    List<BaseNode> nodes;
    List<BaseEdge> edges;

    public abstract float process();
}

abstract class BaseNode
{
    List<BaseEdge> inputs;

    public List<BaseEdge> Inputs
    {
        get
        {
            return inputs;
        }
    }

    public abstract float process();
}

abstract class BaseEdge
{
    BaseNode from;
    BaseNode to;

    public BaseNode To
    {
        get
        {
            return to;
        }
    }

    public BaseNode From
    {
        get
        {
            return from;
        }
    }

    public abstract float process();
}

Node 的实现可能如下所示:

class ImplementedNode : BaseNode
{
    public override float process()
    {
        foreach (ImplementedEdge edge in this.Inputs)
        {
            // Something
        }
    }

    public bool getImplementationSpecificInfo()
    {
        return true;
    }
}

【问题讨论】:

  • 谢谢@marceln。您是指现有的解决方案吗?还是一个实现的例子?
  • 对你来说更容易的。只要问题存在,一个例子就可以了。
  • 对不起,如果我很厚实并且不理解@marceln,但我不完全确定您需要哪些额外信息。包含的代码中存在问题,因为行 where TEdge : BaseEdgewhere TNode : BaseNode 无效,因为 BaseEdge 和 BaseNode 是泛型类型,但不能以 BaseNode&lt;THIS_GENERIC_EDGE_TYPE&gt; 的形式使用。
  • 哦,谢谢@Sahuagin 的标签。
  • 我现在按照@marceln 的要求添加了另一个示例。

标签: c# .net generics polymorphism graph-theory


【解决方案1】:

如果您愿意放松对 BaseEdgeBaseNode 的限制,那么您可以执行以下示例中的操作。

我已经介绍了接口INodeIEdge,所以任何使用它的人都不允许创建BaseEdgeBaseNode 的任何类型的具体子类。

public interface INode
{

}

public interface IEdge
{

}

public abstract class Graph<TNode, TEdge>
    where TNode : BaseNode<TEdge>
    where TEdge : BaseEdge<TNode>
{
    public TNode Root { get; set; }
    public List<TNode> Nodes { get; set; }
    public List<TEdge> Edges { get; set; }
}

public abstract class BaseNode<TEdge> : INode where TEdge: IEdge
{
    List<TEdge> inputs;

    public List<TEdge> Inputs
    {
        get
        {
            return inputs;
        }
    }

    public abstract float process();
}

public abstract class BaseEdge<TNode> : IEdge where TNode: INode
{
    TNode from;
    TNode to;

    public TNode To
    {
        get
        {
            return to;
        }
    }

    public TNode From
    {
        get
        {
            return from;
        }
    }

    public abstract float process();
}


public class ConcreteNode : BaseNode<ConcreteEdge>
{
    public override float process()
    {
        return 0;
    }
}

public class ConcreteEdge : BaseEdge<ConcreteNode>
{
    public override float process()
    {
        return 0;
    }
}

public class ConcreteGraph : Graph<ConcreteNode, ConcreteEdge>
{

}

【讨论】:

  • 谢谢,这可能是我能找到的最接近的解决方案,尽管正如我在上面的 cmets 中所说,这与 BaseNode 和 BaseEdge 也包含方法,因为它们仍然需要在内部进行转换。我自己会继续努力解决这个问题,但除非我想出更好的东西,否则我会接受这个解决方案。
  • 呃,完全忘记了“一时的热度”中的接口。呵呵,这样就好了。
  • @fourpastmidnight 如果BaseNodeBaseEdge 需要在内部进行处理,那么为什么不在其他类中实现每个节点或每个边缘的处理并将它们设置为属性呢?因此,将所有依赖于节点/边缘的代码放在基类之外,否则您最终可能会得到处理每种或大多数具体类型的巨大实现。
  • 是的,我想这实际上可能是要走的路。可以做到,但这意味着更大的重构。从长远来看,这可能是值得的。我看过接口,但由于内部方法问题将它们排除在外。感谢您的所有帮助,并感谢您将我推向明智但令人讨厌的结论。你也是@fourpastmidnight。我会接受这个答案。
  • @NodeDear 很高兴我们能帮到你!!
【解决方案2】:

更新

另一个答案,它提供了某种类型的安全性。

public interface IGraph<TNode, TEdge>
    where TNode : INode
    where TEdge : IEdge
{
    TNode Root { get; set }
    List<TNode> Nodes { get; set; }
    List<TEdge> Edges { get; set; }
}

public interface INode
{
    List<IEdge> Edges { get; set; }
}

public interface INode<TEdge> where TEdge : IEdge
{
    List<TEdge> Edges { get; set; }
}

public interface IEdge
{
    INode To { get; set; }
    INode From { get; set; }
}

public interface IEdge<TNode> where TNode : INode
{
    TNode To { get; set; }
    TNode From { get; set; }
}



public class MyGraph : IGraph<MyNode, MyEdge>
{
    public MyGraph(MyNode root)
    {
        Root = root;
    }

    public MyNode Root { get; set; }
    public List<MyNode> Nodes { get; set; }
    public List<MyEdge> Edges { get; set; }
}

public class MyNode : INode<MyEdge>, INode
{
    public List<MyEdge> Edges { get; set; }

    List<IEdge> INode.Edges
    {
        get { return this.Edges; }
        set { this.Edges = value; }
    }
}

public class MyEdge : IEdge<MyNode>, IEdge
{
    public MyNode To { get; set; }
    public MyNode From { get; set; }

    INode IEdge.To
    {
        get { return this.To; }
        set { this.To = value; }
    }

    INode IEdge.From
    {
        get { return this.From; }
        set { this.From = value; }
    }
}

这没有给我编译错误。

更新

这个答案不起作用。但是,我将它留在这里,以供与它一起出现的 cmets 使用。


@NodeDear,当Graph 是通用的时,我认为您的原始示例是正确的:

Graph<TNode, TEdge> where TNode : BaseNode<TEdge>, TEdge : BaseEdge<TNode>

在您为@marceln 提供的示例中,Graph 类不是通用的。因此,您必须进行各种铸造。但是使用通用版本:

public abstract Graph<TNode, TEdge>
    where TNode : BaseNode<TEdge>
    where TEdge : BaseEdge<TNode>
{
    public TNode Root { get; set; }
    public List<TNode> Nodes { get; set; }
    public List<TEdge> Edges { get; set; }
}

public abstract BaseNode<TEdge> where TEdge : BaseEdge<BaseNode<TEdge>>
{
    public List<TEdge> Edges { get; set; }
}

public abstract BaseEdge<TNode> where TNode : BaseNode<BaseEdge<TNode>>
{
    public TNode To { get; set; }
    public TNode From { get; set; }
}


public class MyNode : BaseNode<TEdge> where TEdge : BaseEdge<MyNode>
{
    ...
}

public class MyEdge : BaseEdge<TNode> where TNode : BaseNode<MyEdge>
{
    ...
}

public class MyGraph : Graph<MyNode, MyEdge>
{
    ...
}

public MyGraph<MyNode, MyEdge> g = new MyGraph<MyNode, MyEdge>();
List<MyEdge> edges = g.Nodes[0].Edges;
MyNode toNode = g.Edges[0].To;

这允许从Graph&lt;TNode, TEdge&gt; 派生的任何类型具有强类型节点和边,这正是 OP 正在寻找的。​​p>

【讨论】:

  • BaseEdgeBaseNode 的定义是什么样的?
  • @marceln Hehe,我现在正在 VS 中查看它,以确保它正确......最重要的是,它可以编译;)
  • :) 我认为如果它有效的话会很棒。但我不明白你如何能绕过这个BaseEdge&lt;TNode&gt; where TNode : BaseNode&lt;BaseEdge&lt;TNode&gt;&gt;。此外,您在示例中缺少 class 关键字。
  • 是的,这就是我坚持使用@marceln 的原因。我希望可能有一些我不知道的技巧,但它看起来并不乐观。
  • 你也许可以做类似的事情,但它需要反射来摆脱铸造......但仍然会在幕后发生铸造。最后,一定有一个更简单的解决方案。
猜你喜欢
  • 2023-02-10
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
相关资源
最近更新 更多