Reputation: 5621
I have a base class called SCO that a number of classes inherit from. I initially just wanted to sort the collection, but based off of some recommendations, it sounds like I can instantiate and sort the collection in one method.
I have this constructor that all base classes inherit:
public SCO(SPListItem item, List<Vote> votes)
{
UpVotes = voteMeta.UpVotes;
DownVotes = voteMeta.DownVotes;
VoteTotal = UpVotes - DownVotes;
HotScore = Calculation.HotScore(Convert.ToInt32(UpVotes), Convert.ToInt32(DownVotes), Convert.ToDateTime(item["Created"]));
}
This is where I get stuck. I cannot instantiate <T>
.
public static List<T> SortedCollection<T>(SPListItemCollection items, ListSortType sortType, List<Vote> votes) where T : SCO
{
var returnlist = new List<T>();
for (int i = 0; i < items.Count; i++) { returnlist.Add(new T(items[i], votes)); }
switch (sortType)
{
// Sort List based on passed ENUM
}
return returnlist;
}
If I can do all of this in one method, I avoid some costly casting AND boxing.
Upvotes: 0
Views: 318
Reputation: 25146
Do you need to pass those value in the constructor?
you could make those settable properties on the type, and then call the default constructor, and after the object is created, just set the properties after...
Upvotes: 0
Reputation: 128357
What's missing here is something more generic than your means for instantiating a T
object:
new T(items[i], votes)
The compiler can't guarantee that this constructor exists for every type T
. What you could do is have the function accept as an additional parameter some factory method of type Func<SPListItem, IList<Vote>, T>
, so that the above can be rewritten:
returnlist.Add(factory(items[i], votes))
Alternately, you could instead implement a factory class which effectively contains this instantiation logic for each type in your code that derives from SCO
, e.g.:
class SCOFactory
{
public T Create<T>(SPListItem item, IList<Vote> votes) where T : SCO
{
// Do your instantiation here.
}
}
You see this quite a lot (it's often called the "Factory Pattern"); it can be a bit ugly as a method (often a giant switch
statement), but at least it contains the ugliness in one place and allows you to write functions such as the one in your question without having to add another parameter and ultimately clutter up a lot of the code in your project.
A word of general caution (to be taken with a grain of salt, of course): overly genericizing your code can ultimately lead to headaches without ever yielding much tangible benefit. Ask yourself before proceeding whether it really is beneficial to you to write this code in such a generic way; if so, either of the suggestions above might prove a decent starting point. If not, consider going with a simpler approach that may be less flexible in theory but easier to work with in practice.
Upvotes: 2
Reputation: 564641
The only constraint allowable in generics is new()
, which will only work if there is a constructor which does not take any parameters.
I have this constructor that all base classes inherit:
The problem is that this isn't enforceable. Sub classes are (and should be) free to define their own constructors, as long as they chain to this constructor. A sub class is free to use whatever construction mechanism required to instantiate that class.
You could work around this via reflection and Activator.CreateInstance, which will allow you to construct the object with parameters. However, this is fairly "ugly" (though, not that ugly considering that using the new() constraint still calls Activator.CreateInstance):
Type genericType = typeof(T);
for (int i = 0; i < items.Count; i++)
{
returnlist.Add((T)Activator.CreateInstance(genericType, new object[] {items[i], votes}));
}
Upvotes: 3