This is an extension of this questionand probably might even be a duplicate of some other question(If so, please forgive me). I see from MSDN that generics are usually used with collections
The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees and so on where operations such as adding and removing items from the collection are performed in much the same way regardless of the type of data being stored.
The examples I have seen also validate the above statement.
Can someone give a valid use of generics in a real-life scenario which does not involve any collections ?
Pedantically, I was thinking about making an example which does not involve collections
public class Animal<T>
{
public void Speak()
{
Console.WriteLine("I am an Animal and my type is " + typeof(T).ToString());
}
public void Eat()
{
//Eat food
}
}
public class Dog
{
public void WhoAmI()
{
Console.WriteLine(this.GetType().ToString());
}
}
and "An Animal of type Dog" will be
Animal<Dog> magic = new Animal<Dog>();
It is entirely possible to have Dog
getting inherited from Animal
(Assuming a non-generic version of Animal
)Dog:Animal
Therefore Dog
is an Animal
Another example I was thinking was a BankAccount
. It can be BankAccount<Checking>
,BankAccount<Savings>
. This can very well be Checking:BankAccount
and Savings:BankAccount
.
Are there any best practices to determine if we should go w开发者_开发问答ith generics or with inheritance ?
You'll probably get some better answers, but meanwhile consider this: Generic classes imply an "of" relation between the generic class and the parameter class.
All dogs are animals, so they share certain attributes/qualities with all other animals. If you use inheritance, then it is very easy to implement those common qualities in the Animal class, and add qualities at decendant classes. But if you implement it using Animal (Of Dog)
, then:
- Your Dog class isn't really a fully-qualified dog/animal by itself, as its "animal qualities" are contained in the
Animal (Of T)
class. The Dog must be tied with the Animal in a parent-child relationship. - Your Animal class isn't really an animal: you cannot create some method which accepts Animals as an argument, because you can't refer to the globalized
Animal (Of T)
class. It is actually a family of classes with a common behavior, not a superclass. You lose the benefit of dealing with Animals outside theAnimal (Of T)
class.
But here is how, IMHO, you should think of generics:
Think about a MedicalDoctor(Of T)
class.
Veterinarian(Of T as Animal)
would be inheriting from MedicalDoctor(Of Animal)
.
DogVeterinarian
would inherit from Veterinarian(Of Dog)
.
Key point: the generic class and the parameter class are not tightly-coupled and co-dependant, they co-exist.
BTW, if you want to see some good non-collection usage of generics, just notice the delegates we have: Action(Of T), Func(Of TResult), Comparison(Of T), EventHandler(Of TEventArgs), Converter(Of TSource, TResult)... and the interfaces: IEqualityComparer(Of T), IComparer(Of T)...
One real world example that know of is in the WCF Channel Factory.
From the page:
public sealed class Test
{
static void Main()
{
// Code not shown.
}
public void Run()
{
// This code is written by an application developer.
// Create a channel factory.
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost/MathService/Ep1");
ChannelFactory<IMath> myChannelFactory = new ChannelFactory<IMath>(myBinding, myEndpoint);
// Create a channel.
IMath wcfClient1 = myChannelFactory.CreateChannel();
double s = wcfClient1.Add(3, 39);
Console.WriteLine(s.ToString());
((IClientChannel)wcfClient1).Close();
// Create another channel.
IMath wcfClient2 = myChannelFactory.CreateChannel();
s = wcfClient2.Add(15, 27);
Console.WriteLine(s.ToString());
((IClientChannel)wcfClient2).Close();
myChannelFactory.Close();
}
}
The way that I think of generics is that they are a represent a type that a class is acting on. For example, the ChannelFactory builds factories of type T. Inheritance represents a hierarchal relationship between types. A dog is an animal, a golden retriever is both a dog and an animal.
Nullable<T>
is generic, isn't a collection class, and can't involve inheritance since it relates to struct
s.
The generic delegate families are quite nice - EventHandler<T>
, Action<...]>
, Func<[...]>
- it is much clearer what Func<int,bool>
is rather than some custom IdPredicate
or whatever.
You're confusing the "is-a" aspect of inheritance with the "of" aspect of generics.
Generics imply the same-ness of operations across classes, not merely that the operations exist polymorphically.
In your Animal example, an instance of Animal<Dog>
does not have a WhoAmI
method, whereas, an instance of Dog : Animal
would.
Use inheritance in your situation, please.
If a class can be described correctly by another class [for example, a square can be described as a rectangle] the square should inherit/extend from the rectangle quest. Otherwise, you're getting into a mess.
Use GenericClass to mean this is a GenericClass object of tType-s
Use Square:Rectangle to mean this is a Square, which is also a Rectangle
In your case:
Use Dog:Animal to mean this is a Dog, which is also an Animal
精彩评论