Search code examples
c#genericscastinginstantiationupcasting

Return a derived object from a generic method


I would like to create a method that can return the generic type defined in the class, here is a detailed example;

https://dotnetfiddle.net/SApVp3

using System;
                    
public class Program
{
    public static void Main()
    {
        // This would be some string imported from a CSV file   
        var customerData = "Customer,1,Ford";       
        var personData = "Person,675,Henry,Ford";
        
        var customerImporter = new ImportData<CompanyMaster>();
        customerImporter.ImportDataFromFile(customerData);
                                      
        var personImporter = new ImportData<PersonMaster>();
        personImporter.ImportDataFromFile(personData);
    }
}

public class GenericRepository<TBase> 
    where TBase : EntityBase
{
    public void Insert(TBase entity)
    {
        //.. generic Insert to database
    }
}

public class ImportData<TBase>  
    where TBase : EntityBase
{
    GenericRepository<TBase> _genericRepository;
    
    //ctor
    public void ImportDataFromFile(string data)
    {
        // convert the string data to TBase
        _genericRepository = new GenericRepository<TBase>();
    }
}

public class CsvConverter<TBase> where TBase: EntityBase{
    
    public TBase ConvertTo(string someString)
    {
        if (someString.StartsWith("Customer"))
        {
            return GetCompany(someString);
        } 
        
        else return GetPerson(someString);
    }
    
    private CompanyMaster GetCompany(string companyString){
        return new CompanyMaster();
    }
    
    private PersonMaster GetPerson(string companyString){
        return new PersonMaster();
    }

}


public abstract class EntityBase
{
    public int Id { get; set; }
    public DateTime CreatedDate { get; set; }
}

public class CompanyMaster : EntityBase
{
    public string CompanyName { get; set; }
}

public class PersonMaster : EntityBase
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

This currently throws;

Compilation error (line 47, col 11): Cannot implicitly convert type 'CompanyMaster' to 'TBase' Compilation error (line 50, col 15): Cannot implicitly convert type 'PersonMaster' to 'TBase'

Can this be made to work?


Solution

  • You need to do an upcast using:

    public TBase ConvertTo(string someString)
    {
      if ( someString.StartsWith("Customer") )
      {
        return (TBase)Convert.ChangeType(GetCompany(someString), typeof(TBase));
      }
      else
      {
        return (TBase)Convert.ChangeType(GetPerson(someString), typeof(TBase));
      }
    }
    

    Or as suggested by @canton7:

    if ( someString.StartsWith("Customer") )
    {
      return (TBase)(object)GetCompany(someString);
    }
    else
    {
      return (TBase)(object)GetPerson(someString);
    }
    

    Difference between casting and using the Convert.To() method