A part of my (C# 3.0 .NET 3.5) application requires several lists of strings to be maintained. I declare them, unsurprisingly, as List<string>
and everything works, which is nice.
The strings in these List
s are actually (and always) Fund IDs. I'm wondering if it might be more intention-revealing to be more explicit, e.g.:
public class FundIdList : List<string> { }
... and this works as well. Are there any obvious drawbacks to this, either technically or philosophically?
Generics is a technique that improves your programs in many ways such as: It helps you in code reuse, performance and type safety. You can create your own generic classes, methods, interfaces and delegates. You can create generic collection classes.
Generic means the general form, not specific. In C#, generic means not specific to a particular data type. C# allows you to define generic classes, interfaces, abstract classes, fields, methods, static methods, properties, events, delegates, and operators using the type parameter and without the specific data type.
First introduced in . NET Framework 2.0, generics are essentially a "code template" that allows developers to define type-safe data structures without committing to an actual data type.
I would start by going in the other direction: wrapping the string up into a class/struct called FundId. The advantage of doing so, I think, is greater than the generic list versus specialised list.
As for FundIdList, the advantage to having such a class is similar to point 3 above for the FundId: you have a place to hook in methods/functions that operate on the list of FundIds (i.e. aggregate functions). Without such a place, you'll find that static helper methods start to crop up throughout the code or, in some static helper class.
List<> has no virtual or protected members - such classes should almost never be subclassed. Also, although it's possible you need the full functionality of List<string>
, if you do - is there much point to making such a subclass?
Subclassing has a variety of downsides. If you declare your local type to be FundIdList
, then you won't be able to assign to it by e.g. using linq and .ToList
since your type is more specific. I've seen people decide they need extra functionality in such lists, and then add it to the subclassed list class. This is problematic, because the List implementation ignores such extra bits and may violate your constraints - e.g. if you demand uniqueness and declare a new Add method, anyone that simply (legally) upcasts to List<string>
for instance by passing the list as a parameter typed as such will use the default list Add, not your new Add. You can only add functionality, never remove it - and there are no protected or virtual members that require subclassing to exploit.
So you can't really add any functionality you couldn't with an extension method, and your types aren't fully compatible anymore which limits what you can do with your list.
I prefer declaring a struct FundId
containing a string and implementing whatever guarantees concerning that string you need there, and then working with a List<FundId>
rather than a List<string>
.
Finally, do you really mean List<>
? I see many people use List<>
for things for which IEnumerable<>
or plain arrays are more suitable. Exposing your internal List
in an api is particularly tricky since that means any API user can add/remove/change items. Even if you copy your list first, such a return value is still misleading, since people might expect to be able to add/remove/change items. And if you're not exposing the List
in an API but merely using it for internal bookkeeping, then it's not nearly as interesting to declare and use a type that adds no functionality, only documentation.
Only use List<>
for internals, and don't subclass it if you do. If you want some explicit type-safety, wrap string
in a struct (not a class, since a struct is more efficient here and has better semantics: there's no confusion between a null FundId
and a null string, and object equality and hashcode work as expected with structs but need to be manually specified for classes). Finally, expose IEnumerable<>
if you need to support enumeration, or if you need indexing as well use the simple ReadOnlyCollection<>
wrapper around your list rather than let the API client fiddle with internal bits. If you really need a mutatable list API, ObservableCollection<>
at least lets you react to changes the client makes.
Personally I would leave it as a List<string>
, or possibly create a FundId
class that wraps a string and then store a List<FundId>
.
The List<FundId>
option would enforce type correct-ness and allow you to put some validation on FundIds
.
Just leave it as a List<string>
, you variable name is enough to tell others that it's storing FundIDs.
var fundIDList = new List<string>();
When do I need to inherit List<T>
?
Inherit it if you have really special actions/operations to do to a fund id list.
public class FundIdList : List<string>
{
public void SpecialAction()
{
//can only do with a fund id list
//sorry I can't give an example :(
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With