4

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 ?

Moe Sisko
  • 11,665
  • 8
  • 50
  • 80

2 Answers2

6

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);
}
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
0

You're going to apply T to FileHelperEngine<T> which already has the same constraint, T : class. The compiler can tell that if you don't put that same constraint on your method then T could be invalid for FileHelperEngine<T>. So it's just preventing type mismatches.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • that's true if one is using FileHelperEngine, as in BoltClock's sample. But it doesn't seem to be the case when using FIleHelperEngine(Type recordType) as in my original example. – Moe Sisko Mar 10 '16 at 23:00