In functional languages (like lisp), you use pattern matching to determine what happens to a particular element in a list. The equivalent in C# would be a chain of if...elseif statements that check the type of an element and perform an operation based on that. Needless to say, functional pattern matching is more efficient than runtime type checking.
Using polymorphism would be a closer match to pattern matching. That is, having the objects of a list match a particular interface and calling a function on that interface for each object. Another alternative would be to provide a series of overloaded methods that take a specific object type as a parameter. The default method taking Object as its parameter.
public class ListVisitor
{
public void DoSomething(IEnumerable<dynamic> list)
{
foreach(dynamic obj in list)
{
DoSomething(obj);
}
}
public void DoSomething(SomeClass obj)
{
//do something with SomeClass
}
public void DoSomething(AnotherClass obj)
{
//do something with AnotherClass
}
public void DoSomething(Object obj)
{
//do something with everything els
}
}
This approach provides an approximation to Lisp pattern matching. The visitor pattern (as implemented here, is a great example of usage for heterogenous lists). Another example would be for message dispatching where, there are listeners for certain messages in a priority queue and using chain of responsibility, the dispatcher passes the message and the first handler that matches the message handles it.
The flip side is notifying everyone who registers for a message (for example the Event Aggregator pattern commonly used for loose coupling of ViewModels in the MVVM pattern). I use the following construct
IDictionary<Type, List<Object>>
The only way to add to the dictionary is a function
Register<T>(Action<T> handler)
(and the object is actually a WeakReference to the passed in handler). So here I HAVE to use List<Object> because at compile time, I don't know what the closed type will be. At Runtime however I can enforce that it will be that Type that is the key for the dictionary. When I want to fire the event I call
Send<T>(T message)
and again I resolve the list. There is no advantage to using List<dynamic> because I need to cast it anyway. So as you see there are merits to both approaches. If you're going to dynamically dispatch an object using Method overloading, dynamic is the way to do it. If you are FORCED to cast regardless, might as well use Object.
List<dynamic>
different (for your question) from simply doingList<object>
?