1

Have a generic function, it doesn't really mater what it is doing (FYI coping one list of object to another), main idea is that it has two types Ts and Tp

        public static List<Tp> CreateAndFillList<Ts, Tp>(this IEnumerable<Ts> sItems) where Tp : class, new()
    {

        Type myType = default(Type);
        PropertyInfo[] pSourceAllInfos = null;
        if (pSourceAllInfos == null)
        {
            myType = typeof(Ts);
            pSourceAllInfos = myType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToArray();
        }
        PropertyInfo[] pTargetAllInfos = null;
        if (pTargetAllInfos == null)
        {
            myType = typeof(Tp);
            pTargetAllInfos = myType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(pi => pi.CanWrite).ToArray();
        }
        var joinedPI = (from spi in pSourceAllInfos
                        join tpi in pTargetAllInfos on spi.Name.ToLower() equals tpi.Name.ToLower()
                        select new { spi, tpi }).ToList();

        List<Tp> retList = new List<Tp>();
        foreach (var sItem in sItems)
        {
            Tp tpNewItem = new Tp();
            foreach (var jpi in joinedPI)
            {
                jpi.tpi.SetValue(tpNewItem, jpi.spi.GetValue(sItem, null), null);
            }
            retList.Add(tpNewItem);
        }
        return retList;
    }

Have two simple classes

 public class SourceInfo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string SourceData { get; set; }
}

public class TargetInfo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string TargetData { get; set; }
}

My problem is that following code throw compilation error

 private void button1_Click(object sender, EventArgs e)
    {

        List<SourceInfo> srcLst = new List<SourceInfo>();
        srcLst.Add(new SourceInfo() { Id = 1, Name = "First", SourceData = "data1" });
        srcLst.Add(new SourceInfo() { Id = 2, Name = "Second", SourceData = "data2" });

        var q = from li in srcLst
                    select new { li.Id, li.Name };

        dynamic qD = from li in srcLst
                select new { li.Id, li.Name };

        var resultLst = srcLst.CreateAndFillList<TargetInfo>(); 
        //Using the generic method 'ExtensionTest.Extensions.CreateAndFillList<Ts,Tp>(System.Collections.Generic.IEnumerable<Ts>)' requires 2 type arguments

        var resultLst1 = q.CreateAndFillList<TargetInfo>(); 
        //Using the generic method 'ExtensionTest.Extensions.CreateAndFillList<Ts,Tp>(System.Collections.Generic.IEnumerable<Ts>)' requires 2 type arguments

        var resultLst2 = qD.CreateAndFillList<TargetInfo>();
        //works but will have to use dynamic... 
    }

And at the same time in VB.Net everything is ok!!!!

        Dim lst As List(Of SourceInfo) = New List(Of SourceInfo)()
    lst.Add(New SourceInfo() With {.Id = 1, .Name = "First"})
    lst.Add(New SourceInfo() With {.Id = 2, .Name = "Second"})

    Dim q = From li In lst
           Select New With {li.Id, li.Name}

    Dim retLst = lst.CreateAndFillList(Of TargetInfo)()

    Dim retLst1 = q.CreateAndFillList(Of TargetInfo)()

My problem is I don't want to use dynamic everywhere because it will require extra coding plus it is run-time compilation.

What I am doing wrong in C#? please help.

user2932688
  • 1,546
  • 11
  • 24

2 Answers2

1

The compiler message is quite clear about the problem: You need to specify both type arguments. If you specify only one, it is unclear which of both parameters it should be.

var resultLst = srcLst.CreateAndFillList<SourceInfo, TargetInfo>();
var resultLst1 = q.CreateAndFillList<SourceInfo, TargetInfo>();

And this:

dynamic qD = from li in srcLst
             select new { li.Id, li.Name };

does not need to be dynamic. var is more appropriate here and will give you compile-time errors. If you do this, you get the same error for qD:

var resultLst2 = qD.CreateAndFillList<SourceInfo, TargetInfo>();

Otherwise, you will get the error only at runtime.

Nico Schertler
  • 32,049
  • 4
  • 39
  • 70
  • I cant specify both types for case when it is anonymous type, Dim q = From li In lst Select New With {li.Id, li.Name} – user2932688 Nov 03 '15 at 16:51
  • So basically what your are saying there no way to do in C# what was easily done in VB.Net... – user2932688 Nov 03 '15 at 16:58
  • It seems so. You can work around by de-coupling the two parameters. E.g. create a generic class that takes the first parameter (you can get type inference via a factory method). Then specify the method with the second type parameter. Depending on your overall scenario, this might not be the most elegant or efficient method. But it does what you want it to do. – Nico Schertler Nov 03 '15 at 18:15
  • Thanks, but if it is not possible I would rather use defined types instead anonymous than build extension over extension workaround. – user2932688 Nov 03 '15 at 18:40
0

What I am doing wrong in C#?

The compiler won't partially infer generic parameters. Using dynamic defers that check to runtime, where it will likely fail. You need to supply both the input and output generic parameters in your call to CreateAndFillList.

var resultLst = srcLst.CreateAndFillList<SourceInfo, TargetInfo>(); 
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • But it does work for VB.Net with only one target Type, and .net infers source type from list – user2932688 Nov 03 '15 at 16:57
  • @user2932688 Have you _executed_ it? `Dim` defers the binding to run-time (similar to dynamic` in C#) I suspect it will fail at run-time. – D Stanley Nov 03 '15 at 17:05
  • D Stanley, I does work in VB.net, believe me or check by yourself. Also I've got the answer that this is impossible for C# so newer-mind. – user2932688 Nov 03 '15 at 18:28
  • I've tried it in LinqPad and get the same compilation error that I get in C# (cannot infer generic parameters). However Linqpad does not allow you to change `Option Infer` settings so that may be what allows it. I'm actually surprised that it works in VB.NET, but then again _lots_ of things work in VB.NET that don't work in C#. – D Stanley Nov 03 '15 at 18:37