1

I'm trying to implement a generic Excel Export.

I've created two Interfaces IData and IDataList. IData provides a function to get the Data transformed to an array and IDataList a function to get the corresponding names of the properties.

The interfaces

public interface IData
{
    string[] GetData();
}

public interface IDataList<IData> : IList<IData>
{
    string[] GetHeader();
}

The classes

public Data : IData
{
    public string col1 { get; set; }
    public string[] GetData()
    {
        return new [col1];
    }
}
public DataList : List<Data>, IDataList<Data>
{
    public string[] GetHeader()
    {
        return new ["col1"];
    }
}

Trying to use it

public void CreateDocument(IDataList<IData> dataList)
{
    // ...
    worksheet.Import(dataList.GetHeader(), RowIdx++, ColIdx, false);

    // ...
    foreach (IData data in dataList)
    {
        worksheet.Import(data.GetData(), RowIdx++, ColIdx, false);
    }
}

DataList data {get;set;} = new DataList();
// ... populate data

CreateDocument(data);

The project won't compile with the error message: Error CS1503 Argument 1: cannot convert from 'DataList' to 'IDataList<IData>'

I was wondering if you could point me in the right direction?

Edit: This question has been marked a duplicate of Why an inherited interface can't be converted to its base interface in generic context?. While that question does explain why I can't call the function the way I've tried it does not help me resolve my issue.

I'm trying to implement the interfaces that I have designed in order to write a generic function WriteDocument(). This means that I require access to the functions GetHeader() and GetData(). Some people have suggested to use IEnumerable instead of IList but I haven't been able to successfully implement that either.

What I have are multiple objects that all have to be written in the same style to an Excel file. The objects have different properties. With the help of GetData() I can convert these properties to a string array which can easily be dumped to the excel file. The GetHeader() function returns the names of these properties in the same order as GetData().

I'm open to a different approach however I'm not sure where to get started. It would be cool to have the WriteDocument() function in a generic way so I don't have to duplicate the code for every object.

Edit2: Instead of trying to solve this with generics there's a much simpler solution. Rewrite CreateDocument—Function:

public void CreateDocument(string[] header, IEnumerable<string[]> data) { }

The classes and interface need a slight change

public interface IHasHeader { string[] GetHeader(); }
public class DataList : List<Data>, IHasHeader { }
public class Data : IData { }

The interfaces aren't actually required anymore at this point.

The function can then be used as follows:

public DataList data = new DataList();
// data.Add(new Data(){ ... })
CreateDocument(data.GetHeader(), data.Select(x => x.GetData()));

I don't have the required reputation to add a solution. If anyone were to convert it to a solution I'd be willing to accept.

user80180
  • 11
  • 3
  • Please can you elaborate on why you want to add a generic to `IDataList`? I appears that you don't use this generic type in the interface definition itself, making it redundant. – JimmyDeemo Jul 29 '19 at 10:28
  • This is the most common mistake i see with generics `Type` is not `IType` they are different (even though it looks like it should work), the best you can hope for is a convenient `IType` which will limit its usage. I think this problem needs to be rethought. Without a more concrete example its hard to know which way you should do this – TheGeneral Jul 29 '19 at 10:32
  • 1
    Let's say I had a `List`? Should I be able to cast it to a `List`? The answer is no - since then I'd be able to `Add` another fruit to it. That shows the general principle. `A` can't be cast to `A`. – mjwills Jul 29 '19 at 10:34
  • Sorry i had a spelling mistake. convenient = covarient – TheGeneral Jul 29 '19 at 10:37
  • Polymorphism doesn't work the same when there are generics somewhere in inheritance hierarachy – Tomas Smagurauskas Jul 29 '19 at 10:44
  • @mjwills and chriz That actually makes a lot of sense and is the core error. – user80180 Jul 29 '19 at 11:11
  • @TheGeneral What I'm trying to achieve is a function to which I can pass a List where T may be any class. They all provide the function 'GetData()' to transform the object to a string array. Which can easily be written to the excel file. – user80180 Jul 29 '19 at 11:13
  • @TomasSmagurauskas - Polymorphism doesn't change when there are generics in the inheritance hierarchy. – Enigmativity Jul 29 '19 at 11:55

1 Answers1

0

You can write a method to take any list by telling it it gets a IEnumerable, the base type of List<T> and IEnumerable<T>. This is a non-generic type. If you'd take IList<SomeType> here, you'd need to specify the type explicitly.

See an example below how to use the IEnumerable in a method and check it:

private class MyClass { }
private class MyClassWithInterface : IMyClass { }
private interface IMyClass { }

static void Main(string[] args)
{
    ITakeLists(new List<MyClass>()); // it is
    ITakeLists(new List<MyClassWithInterface>()); // isInstanceOf
    ITakeLists(new List<int>()); // it is not
    //ITakeLists(new MyClass()); // Error, is not derived from IEnumerable
}

private static void ITakeLists(IEnumerable myList)
{
    if (myList is List<MyClass>)
        Console.WriteLine("it is");
    else if(typeof(IMyClass).IsAssignableFrom(myList.GetType().GetGenericArguments()[0]))
        Console.WriteLine("isInstanceOf");
    else
        Console.WriteLine("it is not :(");
}
Chrᴉz remembers Monica
  • 1,829
  • 1
  • 10
  • 24
  • I'm okay with an Enumerable instead of a List. However I need the function ```GetHeader()```. So I've tried inheriting from an Enumerable: ```public interface IDataList : IEnumerable``` for the implementation I took a list ```public class DataList : List, IDataList```. The parameter type stays the same ```public void CreateDocument(IDataList data) ```. With the same Exception: Cannot convert from ```DataList``` to ```IDataList```. – user80180 Jul 29 '19 at 12:24
  • The point is, you cannot inherit from any other type where the generic parameter is something else. `TypeA` can never be `TypeA`. Why dont you simply derive your `DataList`-class from another interface `IHasHeader`? Then, if you only need this method here, you can call it on that object via this interface `CreateDocument(IHasHeader list)`. – Chrᴉz remembers Monica Jul 29 '19 at 12:41