views:

96

answers:

3

Hi,

In an extension method how do a create an object based on the implementation class. So in the code below I wanted to add an "AddRelationship" extension method, however I'm not sure how within the extension method I can create an Relationship object? i.e. don't want to tie the extension method to this particular implementation of relationship

   public static class TopologyExtns
    {

        public static void AddNode<T>(this ITopology<T> topIf, INode<T> node)
        {
            topIf.Nodes.Add(node.Key, node);
        }

        public static INode<T> FindNode<T>(this ITopology<T> topIf, T searchKey)
        {
            return topIf.Nodes[searchKey];
        }

        public static bool AddRelationship<T>(this ITopology<T> topIf, INode<T> parentNode, INode<T> childNode)
        {
            var rel = new RelationshipImp();  // ** How do I create an object from teh implementation 
            // Add nodes to Relationship
            // Add relationships to Nodes
        }
    }


    public interface ITopology<T>
    {
        //List<INode> Nodes { get; set; }
        Dictionary<T, INode<T> > Nodes { get; set; }
    }

    public interface INode<T> 
    {
        // Properties
        List<IRelationship<T>> Relationships { get; set; }
        T Key { get; }
    }

    public interface IRelationship<T>
    {
        // Parameters
        INode<T> Parent { get; set; }
        INode<T> Child { get; set; }
    }


namespace TopologyLibrary_Client
{

    class RelationshipsImp : IRelationship<string>
    {
        public INode<string> Parent { get; set; }
        public INode<string> Child { get; set; }
    }
}

    public class TopologyImp<T> : ITopology<T>
    {
        public Dictionary<T, INode<T>> Nodes { get; set; }
        public TopologyImp()
        {
            Nodes = new Dictionary<T, INode<T>>();
        }

    }

thanks

+2  A: 

You could add an extra type parameter representing the relationship implementation class, specifying the constraints that it must be an IRelationship and have a parameterless constructor:

public static bool AddRelationship<T,R>(this ITopology<T> topIf, INode<T> parentNode, INode<T> childNode) 
    where R : IRelationship, new() {
    var rel = new R();
    // ...
}

Then you should call it like this to specify the IRelationship concrete type (the 'R' type parameter):

topology.AddRelationship<string, RelationshipImp>(parentNode, childNode);

EDIT: alternative approach, without extension method: you define (and later instantiate) a RelationshipFactory class:

class RelationshipFactory<R> 
    where R : IRelationship, new(){
    // no longer an extension method:
    public static bool AddRelationship<T>(ITopology<T> topIf, INode<T> parentNode, INode<T> childNode) {
        var rel = new R();
        // ...
    }
}
Paolo Tedesco
+1 - that's the easiest way by far.
Andras Zoltan
when I try this VS is trying to tell me when I call this from client code there should be <> after the AddRelationship method name?
Greg
could you possibly give an example of using this method from the client side please
Greg
@Greg: isn't `topology.AddRelationship<RelationshipImp>` the example you wanted?
Paolo Tedesco
sorry - must have overlooked - so I don't suppose there is a way however to not have to supply the <RelationshipImp> for the client usage? (i.e. to keep usage very simple) Or is this not possible with the generics/extension method approach?
Greg
The knowledge of the 'Imp' type must come from somewhere, if you don't want to hardcode it in the extension method. If you want to use an extension method, then I see no other possible solution...
Paolo Tedesco
PS I'm finding I have to actually have 2 type parameters to get it to compile too - so this seems to compile ok "top.AddRelationship<string, RelationshipsImp>(node,node2);"
Greg
@Greg: Yes, you are right, either you specify them all or none if they can be guessed...
Paolo Tedesco
+1  A: 

One way would be to push the responsibility to the ITopology<T> or INode<T> classes by modifying their interfaces to support an IRelationship<T> factory method, e.g. ITopology<T> could be modified as such:

public interface ITopology<T>
{
    IRleationship<T> CreateRelationship(INode<T> parent, INode<T> child);
    Dictionary<T, INode<T> > Nodes { get; set; }
}

And then AddRelationship<T> would look something like:

public static bool AddRelationship<T>(this ITopology<T> topIf, INode<T> parentNode, INode<T> childNode)
{
    var relationship = topIf->CreateRelationship(parentNode, childNode);
    // ...
}

Since you're using extension methods, modifying the interfaces might not be possible or desired, but it's an option.

Chris Schmich
Chris - so in this case the actual implementation of CreateReleatinoship would be in the ITopology implementation not the extension method then?
Greg
Yes, the creation of relationships would be the responsibility of whomever implements `ITopology<T>`. You could also push this into the `INode<T>` interface if it makes more sense.
Chris Schmich
+1  A: 

Addressing another (possible) problem about your design. Why are you using extension methods when you could simply put the AddRelationship method within your Topology implementation? Is there a specific need to use Extension Methods in this case? Also, is there ever going to be a case where a single node can have many parents?

I'd think something like this would be in order:

public interface INode<T> 
{
    // Properties
    INode<T> Parent { get; }
    IEnumerable<INode<T>> Children { get; }
    String Key { get; }

    void AddChild(INode<T> child);
}

public class Node<T> : INode<T>
{
    public Node(String key) : this(key, null) {}
    public Node(String key, INode<T> parent)
    {
        this.Parent = parent;
        this.Children = new List<T>();
        this.Key = key; 
    }

    public virtual INode<T> Parent { get; protected set; }
    public virtual String Key { get; protected set; }
    public virtual List<T> Children { get; protected set; }

    public void AddChild(INode<T> node) 
    {
        this.Children.Add(node);
    }
}

No need for extension methods, or the intermediate IRelationship class. This all hinges on the ability to modify the interface for one, and that an INode can only have one parent.

Edit (Based on Comment from OP):

Considering that you're specifically after making the API for the extension method looking cleaner, you could perhaps do this:

public static bool AddRelationship(this INode<T> node, IRelationship<T> relationship) 
{
    if (node.Relationships == null)
        node.Relationships = new List<T>;

    if (relationship == null) throw new ArgumentNullException("relationship");

    node.Relationships.Add(relationship);
    return true; // I'd make this method void
}

And then calling this would be:

INode node = new Node<String>("some key");
INode someParent = new Node<String>("some parent key");
INode someChild = new Node<String>("some child key");

node.AddRelationship(new RelationshipImp(someParent, someChild));

I haven't tested this, so I may be off with where the generics are specified. I don't know if the inference engine can deduce what it needs to here.

As a point of interest, why did you decide to go with a "node has many parents and many children" instead of "node has neighbours"? I've recently built a graph structure and I found a Dictionary or List of Nodes more than adequate for modeling the directions. Just reading over the original question again - are the relationships supposed to be stored in the Topology? That'd make a lot more sense, and is what it seems you're trying to do with your original extension method.

In the case where the relationships reside in the Topology then I'd go with the other answer posted:

public static bool AddRelationship<T,R>(this ITopology<T> top, INode<T> parent, INode<T> child ) where R : IRelationship, new
{
    IRelationship<T> rel = new R();
    rel.Parent = parent;
    rel.Child = child;
    top.Relationships.Add(rel);
}

And calling this:

ITopology<String> top = new TopologyImp<String>;
top.AddRelationship<RelationshipImp>(new NodeImp("parent"), new NodeImp("Child"));
Josh Smeaton
Hi Josh - in a graph nodes can have multiple parents. The original concept I have is that the Node/Relationship implementations are already there and could be named anything (or the key is any type), but I could use extn methods to add the graph type search routines etc. Paolo's answer is great except the client code would need to specify the types when calling which is a bit ugly for an "easy to use" extension library. Perhaps I need to relax my requirement for the library and include an abstract base class but using generics for the key (so the key type can still be different).
Greg
Greg