Generics and type constraints
-
Hi, I'm toying around with generics and have run into a little problem. My class is a tree, but this is not about whether or not one should make a tree this way - what I'm trying to do adds complexity for just a little lazy-coding benefit, so please ignore the particular example - I just want to understand if what I wanted to do can be done with generics. Here's the TreeNode class:
abstract public class TreeNode<T>
{
TreeNode<T> parent;
List<TreeNode<T>> children = new List<TreeNode<T>>();
T data;public TreeNode() { } public T Data { get { return data; } set { data = value; } } public TreeNode<T> Parent { get { return parent; } set { parent = value; } } public List<TreeNode<T>> Children { get { return children; } set { children = value; } }
}
Now, let's say we want to derive a class for organizing tags into trees. The data is just a string, and for convenience we may want a constructor that sets the text of the node. I'm trying to see if there is a way to write a utility method that would take any treenode (necessarily derived since the base class is abstract) and a collection of node *data* objects and create child nodes with the data and parent set. The following snippet illustrates the Tag and how code might use the utility method to create a hard-coded hierarchy of tags:
public class Tag : TreeNode<string>
{
public Tag() {}
public Tag(string text) { Data = text; }
}class usingTags
{
Tag getBuiltIn()
{
Tag root = new Tag("Built-in");
Tag[] top = TreeUtil.Add<Tag>(root, "License", "Product");
TreeUtil.Add<Tag>(top[0], "CPOL", "MIT", "Proprietary");
TreeUtil.Add<Tag>(top[1], "Banco", "Acco", "Piccolo");
return root;
}
}Based on this, the utility method should be something like this:
static public TNode[] Add<TNode, T>(TNode parent, params T[] data) where TNode : TreeNode<T>, new()
{
List<TNode> list;if (parent == null) list = parent.Children; else list = new List<TNode>(); int startIndex = list.Count; foreach (T item in data) list.Add(new TNode() { Data = item }); return list.GetRange(startIndex, list.Count - startIndex).ToArray();
}
Two problems present themselves:
-
Hi, I'm toying around with generics and have run into a little problem. My class is a tree, but this is not about whether or not one should make a tree this way - what I'm trying to do adds complexity for just a little lazy-coding benefit, so please ignore the particular example - I just want to understand if what I wanted to do can be done with generics. Here's the TreeNode class:
abstract public class TreeNode<T>
{
TreeNode<T> parent;
List<TreeNode<T>> children = new List<TreeNode<T>>();
T data;public TreeNode() { } public T Data { get { return data; } set { data = value; } } public TreeNode<T> Parent { get { return parent; } set { parent = value; } } public List<TreeNode<T>> Children { get { return children; } set { children = value; } }
}
Now, let's say we want to derive a class for organizing tags into trees. The data is just a string, and for convenience we may want a constructor that sets the text of the node. I'm trying to see if there is a way to write a utility method that would take any treenode (necessarily derived since the base class is abstract) and a collection of node *data* objects and create child nodes with the data and parent set. The following snippet illustrates the Tag and how code might use the utility method to create a hard-coded hierarchy of tags:
public class Tag : TreeNode<string>
{
public Tag() {}
public Tag(string text) { Data = text; }
}class usingTags
{
Tag getBuiltIn()
{
Tag root = new Tag("Built-in");
Tag[] top = TreeUtil.Add<Tag>(root, "License", "Product");
TreeUtil.Add<Tag>(top[0], "CPOL", "MIT", "Proprietary");
TreeUtil.Add<Tag>(top[1], "Banco", "Acco", "Piccolo");
return root;
}
}Based on this, the utility method should be something like this:
static public TNode[] Add<TNode, T>(TNode parent, params T[] data) where TNode : TreeNode<T>, new()
{
List<TNode> list;if (parent == null) list = parent.Children; else list = new List<TNode>(); int startIndex = list.Count; foreach (T item in data) list.Add(new TNode() { Data = item }); return list.GetRange(startIndex, list.Count - startIndex).ToArray();
}
Two problems present themselves:
You might want to edit that, pasting your code again with "Encode HTML tags when pasting" set. Gets me every time, too (and normaly the other way - set when I want it off - as well).
No trees were harmed in the sending of this message; however, a significant number of electrons were slightly inconvenienced. This message is made of fully recyclable Zeros and Ones
-
You might want to edit that, pasting your code again with "Encode HTML tags when pasting" set. Gets me every time, too (and normaly the other way - set when I want it off - as well).
No trees were harmed in the sending of this message; however, a significant number of electrons were slightly inconvenienced. This message is made of fully recyclable Zeros and Ones
The problem is that it then does that to the <pre> tags as well... But I guess that is at least preferrable to losing all the generics "tags". Thanks for pointing it out.
-
The problem is that it then does that to the <pre> tags as well... But I guess that is at least preferrable to losing all the generics "tags". Thanks for pointing it out.
It's "Do not interpret HTML tags" that screws up pre tags in a post. Encode HTML tags when pasting only affects stuff in your paste buffer, and normally you don't copy/paste pre tags.
The European Way of War: Blow your own continent up. The American Way of War: Go over and help them.
-
It's "Do not interpret HTML tags" that screws up pre tags in a post. Encode HTML tags when pasting only affects stuff in your paste buffer, and normally you don't copy/paste pre tags.
The European Way of War: Blow your own continent up. The American Way of War: Go over and help them.
Well, encode when pasting usually screws up too because I will frequently cut and paste around text as I'm writing a post, leading to < becoming < (or < in "source view") and so on. I personally think it a bit incredulous that this format-on-paste should be necessary; most other forums have solved this problem by using square brackets for a few basic formatting tags ([b], [i], and so on). Perhaps a code forum would need a slightly more obscure syntax since code can contain all sorts of text, but imho the current solution is not on par with the average forum out there. In any case, I edited my post after it was brought to my attention there was a formatting issue, so if anyone has anything to say about my actual issue I would be interested to hear from them. :)
-
Well, encode when pasting usually screws up too because I will frequently cut and paste around text as I'm writing a post, leading to < becoming < (or < in "source view") and so on. I personally think it a bit incredulous that this format-on-paste should be necessary; most other forums have solved this problem by using square brackets for a few basic formatting tags ([b], [i], and so on). Perhaps a code forum would need a slightly more obscure syntax since code can contain all sorts of text, but imho the current solution is not on par with the average forum out there. In any case, I edited my post after it was brought to my attention there was a formatting issue, so if anyone has anything to say about my actual issue I would be interested to hear from them. :)
dojohansen wrote:
most other forums have solved this problem by using square brackets for a few basic formatting tags
True, I suppose teh bigger question is if html input on CP is default deny or still default allow. I know in the past there were problems with idiots using bits of html to intentionally break a forum page, but I'm not sure how they were ultimately fixed. I haven't done enough with templating to be able to answer the question itself, sorry.
The European Way of War: Blow your own continent up. The American Way of War: Go over and help them.
-
Hi, I'm toying around with generics and have run into a little problem. My class is a tree, but this is not about whether or not one should make a tree this way - what I'm trying to do adds complexity for just a little lazy-coding benefit, so please ignore the particular example - I just want to understand if what I wanted to do can be done with generics. Here's the TreeNode class:
abstract public class TreeNode<T>
{
TreeNode<T> parent;
List<TreeNode<T>> children = new List<TreeNode<T>>();
T data;public TreeNode() { } public T Data { get { return data; } set { data = value; } } public TreeNode<T> Parent { get { return parent; } set { parent = value; } } public List<TreeNode<T>> Children { get { return children; } set { children = value; } }
}
Now, let's say we want to derive a class for organizing tags into trees. The data is just a string, and for convenience we may want a constructor that sets the text of the node. I'm trying to see if there is a way to write a utility method that would take any treenode (necessarily derived since the base class is abstract) and a collection of node *data* objects and create child nodes with the data and parent set. The following snippet illustrates the Tag and how code might use the utility method to create a hard-coded hierarchy of tags:
public class Tag : TreeNode<string>
{
public Tag() {}
public Tag(string text) { Data = text; }
}class usingTags
{
Tag getBuiltIn()
{
Tag root = new Tag("Built-in");
Tag[] top = TreeUtil.Add<Tag>(root, "License", "Product");
TreeUtil.Add<Tag>(top[0], "CPOL", "MIT", "Proprietary");
TreeUtil.Add<Tag>(top[1], "Banco", "Acco", "Piccolo");
return root;
}
}Based on this, the utility method should be something like this:
static public TNode[] Add<TNode, T>(TNode parent, params T[] data) where TNode : TreeNode<T>, new()
{
List<TNode> list;if (parent == null) list = parent.Children; else list = new List<TNode>(); int startIndex = list.Count; foreach (T item in data) list.Add(new TNode() { Data = item }); return list.GetRange(startIndex, list.Count - startIndex).ToArray();
}
Two problems present themselves:
The problem is that just because TNode derives from TreeNode<T>, they may not be the same type. This is the same reason you can't do something like:
List<Stream> list = new List<MemoryStream>();
Did you try something like this:
static public TreeNode<T>[] Add<T>(TreeNode<T> parent, params T[] data)
-
The problem is that just because TNode derives from TreeNode<T>, they may not be the same type. This is the same reason you can't do something like:
List<Stream> list = new List<MemoryStream>();
Did you try something like this:
static public TreeNode<T>[] Add<T>(TreeNode<T> parent, params T[] data)
Thanks for the reply. I see now that the normal rules of assignment are violated; List<TDerived> is not a List<TBase> even if TDerived derives from TBase. They are just two different constructed types, and since it is so there is no way to convert between them. The problem with using
static public TreeNode<T>[] Add<T>(TreeNode<T> parent, params T[] data)
is that I cannot put a type constraint on the constructed type; in order to add new nodes the constructed type must have a default constructor. This problem can be solved by keeping the method declaration but use aList<TBase>
as local variable instead:static public TNode[] Add<TNode, T>(TNode parent, params T[] data) where TNode : TreeNode<T>, new()
{
List<TreeNode<T>> list;if (parent != null) list = parent.Children; else list = new List<TreeNode<T>>(); int startIndex = list.Count; foreach (T item in data) list.Add(new TNode() { Data = item }); return (TNode\[\])list.GetRange(startIndex, list.Count - startIndex).ToArray();
}
In the end it's all futile though. Basically, I wanted to write a generic TreeNode type which can be parameterized by type of node data. But rather than the use constructed types directly, such as TreeNode<string>, I wanted to derive non-generic types from constructed types, potentially adding functionality to each type of tree node, as in
class Tag : TreeNode<string> { ... }
, but I didn't stop to think; Tag.Children would still be List<TreeNode<string>> rather than List<Tag>, the desired type.