Best way to get runtime Type of T?

Given the example code below, I'd like to get the runtime type of T inside a generic method (in this case retrieveData). When I create an XMLFilePersisting I use the interface, namely ITransferable rather than the implementing Types since in my client I want to have a list of all of them together:

List<XMLFilePersisting<ITransferable>> allThem;

So if in retrieveData I do typeof(T) that should return ITransferable which doesn't give me enough info to load the items from the file. The only way I could figure out how to do this was by passing an instance of the implementor of ITransferable in the constructor, but this kind of feels like there should be a better way. Suggestions?

public interface IPersisting<T> {
        List<T> retrieveData();
        bool persistData(List<T> lst);
    }

public interface ITransferable {
    Type getRuntimeType();
}

public class XMLFilePersisting<T> : IPersisting<T> where T : ITransferable {
    private readonly T defaultT;
    //...
    public XMLFilePersisting(string thefile, string thepath, T def) {
        //...
        defaultT = def;
    }
    ...

    public List<T> retrieveData() {
      List<T> retVal = new List<T>();
      Type runType = defaultT.getRuntimeType();
      string elemName = runType.Name;
      using (FileStream fs = new FileStream(FQ_FILE_PATH, FileMode.Open, FileAccess.Read)) {
        using (XmlReader reader = XmlReader.Create(fs)) {
           //below line won't work, typeof resolves at compileTime      
           //XmlSerializer xmlSer = new XmlSerializer(typeof(T));
           XmlSerializer xmlSer = new XmlSerializer(runType);
           if (reader.ReadToFollowing(elemName)) { //go to the first test, skipping over ListofT
              do {
                 T testVal = (T)xmlSer.Deserialize(reader);
                 retVal.Add(testVal);
                }
              while (reader.ReadToNextSibling(elemName));
           }
         } //end using xmlreader
      } //end using FileStream
    return retVal;
  } //end retrieveData
} //end class XMLFilePersisting

Additional info:

Client code looks like below. As you can see I need all

IPersisting<ITransferable>

instances, but the problem is in retrieveData, that makes typeof(T) = ITransferable which doesn't give me enough info to do the deserialization. That is why I pass concrete implmentations of ITransferable to the constructor (MyClass1, MyClass2). This seems to work, but feels like a hack.

IPersisting<ITransferable> xfpMC1 = new XMLFilePersisting<ITransferable>("persistedMC1.xml", myTempDirectory, new MyClass1());
IPersisting<ITransferable> xfpMC2 = new XMLFilePersisting<ITransferable>("persistedMC2.xml", myTempDirectory, new MyClass2());

A

Jon Skeet
people
quotationmark

I would suggest that you make each XMLFilePersisting use the specific concrete type, but then combine the results into a List<ITransferable>. For example:

// Names changed to be more conventional
var class1Loader = new XmlFilePersister<MyClass1>("MC1.xml", myTempDirectory");
var class2Loader = new XmlFilePersister<MyClass2>("MC2.xml", myTempDirectory");

// Could do all of this in one statement... note that this uses the
// covariance of IEnumerable<T>
IEnumerable<ITransferable> class1Results = class1Loader.RetrieveData();
IEnumerable<ITransferable> class2Results = class2Loader.RetrieveData();
var allResults = class1Results.Concat(class2Results).ToList();

Having misunderstood the question slightly, if it's the persisters you want to be in a list, you could make XMLFilePersisting<T> implement IPersisting<ITransferable> - although then you'd have problems when you try to store the data instead of reading it... because you'd need to cast from ITransferable to T, which could obviously fail at execution time.

Fundamentally, I wonder whether you should have two interfaces: IDeserializer<out T> and ISerializer<in T>. With those covariant and contravariant interfaces, you could easily have a List<IDeserializer<ITransferable>> without losing information or needing execution-time checking.

people

See more on this question at Stackoverflow