The LasyList<T> only works with an IQueryable<T> source. It is an implementation of IList<T> and works by populating a private List with all results from the specified IQueryable<T>. The initialization occurs the first time you access any of the IList<T> members.
Example usage would be
var myList = new LazyList(products.Where(p => p.Name.StartsWith("T"));
//initialization occurs here
Console.Write(myList.Count);
The System.Lazy<T> class works with any type and is not limited to IQueryable<T>. Lazy initialization occurs the first time the Lazy<T>.Value property is accessed or the Lazy<T>.ToString method is called.
Example usage would be
var lazyString = new Lazy(() => "Hello world");
//initialization occurs here
Console.Write(lazyString.Value);
We could rewrite the LazyList<T> example to use Lazy<T> as follows:
var myList = new Lazy(() => products.Where(p => p.Name.StartsWith("T").ToList());
//initialization occurs here
Console.Write(myList.Value.Count);
In short: LazyList<T> only works with IQueryable<T>, Lazy<T> works with any type.
LazyList<T> is for the specific use case of when you want the results of an IQueryable<T> as a List<T>, but you don't want the evaluation to occur until you use it.
UPDATE to answer expanded question:
Would you recommend one over the other if I wanted to operate on IQueryable as a List<T>?
I'm assuming that since Lazy<T> is in the framework now, it is a safer bet for future support and maintainability?
Personally I wouldn't use either. If you've got an IQueryable I would keep it as an IQueryable to maximise your flexibility. By keeping the IQueryable you still get access to LINQ to SQL's query comprehension (as long as the context is still alive).
For example, if you call .ToList() on an IQueryable you are asking LINQ to SQL to select all of the columns from the target table and hydrate all of the results (this could be very expensive, especially if you've got thousands of results). This will get translated to something like "SELECT * FROM MyTable".
If you call .Count() on the IQueryable you are asking LINQ to SQL to just get the number of results, this will get translated to something like "SELECT COUNT(*) FROM MyTable". Doing this is much more efficient than hydrating all of the results and then counting them, especially if you're only interested in the number!
By using .Where() on the IQueryable LINQ to SQL will add your conditions to the WHERE clause in the SQL query. This means that you will only pull the data from SQL that you're interested in, rather than hydrating results that you have no intention of using.
You see, by keeping the IQueryable you make things much more flexible for yourself. The majority of the time it will give you better performance than hydrating the entire result set.
If I want to use a strong type instead of an anonymous (var) type would the following statements be functionally equivalent?
Lazy<List<Product>> products = new Lazy<List<Product>>();
LazyList<Product> products = new LazyList<Product>();
I think you're getting anonymous typing confused with implicit typing. A variable declared using the var keyword is implicitly typed to match the type that is being assigned to it. It is strongly typed and therefore cannot be changed after the initial assignment.
The two statements are not functionally equivalent. The LazyList<Product> is an IList<Product>, wheras the Lazy<List<Product>> is a wrapper that contains a List<Product>. Because you're specifically interested in operating on a lazy evaluated list, I'd say the LazyList is probably more specific for your purposes.
You should ask yourself if you really need an actual List of products. If there isn't a compelling reason for having an actual List I'd stick with the IQueryable.