Cannot implicitly convert type System.Collections.Generic.Dictionary<System.Tuple<int,int,string>, AnonymousType#1>

StackOverflow https://stackoverflow.com/questions/11958393

  •  26-06-2021
  •  | 
  •  

Question

I have the following dictionary in a method:

var nmDict = xelem.Descendants(plantNS + "Month").ToDictionary(
    k => new Tuple<int, int, string>(int.Parse(k.Ancestors(plantNS + "Year").First().Attribute("Year").Value), Int32.Parse(k.Attribute("Month1").Value), k.Ancestors(plantNS + "Report").First().Attribute("Location").Value.ToString()),
    v => { 
             var detail = v.Descendants(plantNS + "Details").First();                
             return
                 new
                    {
                      BaseHours = detail.Attribute("BaseHours").Value,
                      OvertimeHours = detail.Attribute("OvertimeHours").Value
                    };
         });

I need to return nmDict. The problem is that I cannot figure out how to label my method signature. I have tried the following:

protected IDictionary<XElement, XElement> OvertimereportData(HarvestTargetTimeRangeUTC ranges)

The above gives me this error:

Cannot implicitly convert type System.Collections.Generic.Dictionary<System.Tuple<int,int,string>,AnonymousType#1>' to 'System.Collections.Generic.IDictionary<System.Xml.Linq.XElement,System.Xml.Linq.XElement>'. An explicit conversion exists (are you missing a cast?) 


protected IDictionary<Tuple, XElement> OvertimereportData(HarvestTargetTimeRangeUTC ranges)

gives me this error:

'System.Tuple': static types cannot be used as type arguments   

I do not know what to do.

Was it helpful?

Solution

When you call the ToDictionary method, the resulting dictionary's type has little to do with the type of elements in your source sequence. It's defined entirely by the data types returned by the key and value expressions you supply to the call. For example, if you were to call:

xelem.Descendants(plantNS + "Month").ToDictionary(
  k => int.Parse(k.Attribute("Year").Value),
  v => k.Attribute("Year).Value
);

You would get an IDictionary<int, string> because that's what your two expressions returned. To return that from a method, you just need to construct the correct type, based on your expressions.

Your first one is easy:

k => new Tuple<int, int, string>(...)

The second one, though, is going to be a problem. The values in your dictionary are of an anonymous type: you return a new { } without specifying a concrete type name for that value. In general, that is going to make it impossible for you to use that dictionary as a return value or parameter. (It can be done, using some very strange-looking generic techniques, but I wouldn't recommend it.)

The first thing you'll need to do, then, is make a concrete type to hold your values, e.g.

public class HoursContainer 
{
  public string BaseHours { get; set; }
  public string OvertimeHouse { get; set; }
}

and change your Linq query appropriately:

var detail = v.Descendants(plantNS + "Details").First();                
return new HoursContainer
{
    BaseHours = detail.Attribute("BaseHours").Value,
    OvertimeHours = detail.Attribute("OvertimeHours").Value
};

Once you've done this, your dictionary will have a concrete type based on the types of things you specified when you created it:

IDictionary<Tuple<int, int, string>, HoursContainer>

(Note: You could also just use another Tuple<int, int> or whatever here, if you wanted, but the resulting generic type would get unwieldy very fast.)

OTHER TIPS

The short answer: You can't return anonymous types from a function.

The long answer: Your dictionary's value type is anonymous {BaseHours, OvertimeHours} which cannot be returned from a function or passed as an argument (except as an object, but that does nobody any good unless you go through the hassle of reflecting into it). Either define a class/struct with BaseHours and OvertimeHours in it, or use a tuple. The former is probably slightly better because you can keep the names BaseHours and OvertimeHours; with a tuple you just get Value1 and Value2.

If you are using C# 4.0 than you can return the anonymous via dynamic type. So your method signature would look like this

protected IDictionary<Tuple<int,int,string>, dynamic> OvertimereportData(HarvestTargetTimeRangeUTC ranges)

And through the dynamic object you can find the properties at run time.

Hope this will help you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top