Search code examples
c#.netgenericsfilehelpers

Why is "where T : class" required in this example?


Sample code :

using System.Collections.Generic;
using FileHelpers;

....
private void Save<T>(string destFilename,  IEnumerable<T> data) where T : class    
{
    var engine = new FileHelperEngine((typeof(T)));


    engine.HeaderText = engine.GetFileHeader(); 
    engine.WriteFile(destFilename, data); // XX
}

At line XX, the 2nd parameter to engine.WriteFile is expecting an IEnumerable<object>. This code works ok.

My question is, why is the "where T : class" constraint required on the method ? If I remove it, I get the compile time error :

Argument 2: cannot convert from
'System.Collections.Generic.IEnumerable<T>' to
'System.Collections.Generic.IEnumerable<object>'

I would have thought that everything was an "object", and so that the constraint was not necessary ?


Solution

  • The constraint is needed because object is a reference type only; the reason value types can be assigned to object is due to boxing (even though, technically, all types inherit from System.Object).

    But boxing is a separate issue from type parameter variance; an IEnumerable<T> with an unconstrained T cannot be converted to an IEnumerable<object> because variance is not supported for value types.

    As an aside, FileHelperEngine<T>, which the non-generic FileHelperEngine inherits from (as FileHelperEngine<object>), also has a T : class constraint. So you're not missing any functionality by having the constraint since only reference types are supported anyway — one could theoretically just use FileHelperEngine<T> directly without going through the non-generic class, since the method given in the sample is already generic:

    using System.Collections.Generic;
    using FileHelpers;
    
    ....
    private void Save<T>(string destFilename,  IEnumerable<T> data) where T : class    
    {
        var engine = new FileHelperEngine<T>();
    
    
        engine.HeaderText = engine.GetFileHeader(); 
        engine.WriteFile(destFilename, data);
    }