Reputation: 1247
I need to return a genericList templateFields
as below from a generic list with code as below:
public interface TestData
{
string field { get; set; }
string fieldName { get; set; }
string type { get; set; }
}
private static IList<T> GETCG<T>(string test, string type) where T : Program.TestData
{
XmlNodeList extractNode = xdoc.SelectNodes(
@".//mediaInstances/mediaInstance/properties/templateFields/templateField", manager);
var nodees = new List<XmlNode>(extractNode.Cast<XmlNode>());
var templateFields = nodees.Cast<XmlNode>().Select(x => new
{
field = (String)x.Attributes["userName"].Value,
fieldName = (String)x.Attributes["name"].Value
.Substring(0, x.Attributes["name"].Value.IndexOf(':')),
type = (String)x.Attributes["name"].Value.Substring(x.Attributes["name"].Value
.IndexOf(':') + 1, 4)
}).ToList();
}
return (T)Convert.ChangeType(templateFields, typeof(T));
I get the following error, on the return:
Object must implement Iconvertible.
I do understand templateFields
doesnot implement IConvertible to use ChangeType. What's the best way of returning templateFields
Upvotes: 0
Views: 521
Reputation: 112762
You declared an interface TestData
but didn't declare any type implementing it. You cannot cast any type that just happens to have the same properties by accident to this interface. You must create a class or struct implementing it. Also, with the usual .NET naming conventions interface names start with an upper case I
and property names have PascalCase.
With these declarations ...
public interface ITestData
{
string Field { get; set; }
string FieldName { get; set; }
string Type { get; set; }
}
public class TestData : ITestData
{
public string Field { get; set; }
public string FieldName { get; set; }
public string Type { get; set; }
}
You can write
private static IList<ITestData> GETCG(string test, string type)
{
XmlNodeList extractNode = xdoc.SelectNodes(
@".//mediaInstances/mediaInstance/properties/templateFields/templateField", manager);
var nodees = new List<XmlNode>(extractNode.Cast<XmlNode>());
var templateFields = nodees.Cast<XmlNode>().Select(x => (ITestData)new TestData {
Field = (String)x.Attributes["userName"].Value,
FieldName = (String)x.Attributes["name"].Value
.Substring(0, x.Attributes["name"].Value.IndexOf(':')),
Type = (String)x.Attributes["name"].Value.Substring(x.Attributes["name"].Value
.IndexOf(':') + 1, 4)
}).ToList();
return templateFields;
}
Note that the method is not generic. To make .ToList()
create a IList<ITestData>
, the new data must be casted to the interface (ITestData)new TestData { ... }
.
The question is whether you still need the interface, or if you prefer to use the class directly.
If you still want the method to be generic, you must tell it that T
must have a default constructor with the new()
constraint. And you must call the method with a concrete type. I.e., you cannot call it with the interface, since this one does not have a constructor.
private static IList<T> GETCG<T>(string test, string type)
where T : ITestData, new()
{
...
var templateFields = nodees.Cast<XmlNode>().Select(x => new T {
...
}).ToList();
return templateFields;
}
and call with
IList<TestData> var result = GETCG<TestData>("hello", "world");
Upvotes: 0
Reputation: 37070
I think the problem here is that you're selecting an anonymous type when you do select new { ... }
, and then the Convert.ChangeType
fails because anonymous types only include public read-only
properties, and don't implement IConvertible
. Instead, we want to select a new T
. But in order to do this, we also have to include a new() constraint on T
, which means that T
must have a default constructor (so we can create an instance of it).
By doing this, we don't need to convert anything, as we have a List<T>
as a result of the Select
.
You can also reduce some code by selecting an IEnumerable<XmlNode>
in one line, rather than creating a second variable and doing a cast
on the first one.
Something like this should work:
private static IList<T> GETCG<T>(string test, string type) where T : TestData, new()
{
IEnumerable<XmlNode> templateFieldNodes = xdoc
.SelectNodes(".//mediaInstances/mediaInstance/properties/templateFields/templateField",
manager)
.Cast<XmlNode>();
return templateFieldNodes.Select(x => new T
{
field = (String)x.Attributes["userName"].Value,
fieldName = (String)x.Attributes["name"].Value
.Substring(0, x.Attributes["name"].Value.IndexOf(':')),
type = (String)x.Attributes["name"].Value.Substring(x.Attributes["name"].Value
.IndexOf(':') + 1, 4)
}).ToList();
}
Upvotes: 0
Reputation: 9642
Add new()
contraint to T
and use the following codee
private static IList<T> GETCG<T>(string test, string type) where T : TestData, new()
{
XmlNodeList extractNode = xdoc.SelectNodes(@".//mediaInstances/mediaInstance/properties/templateFields/templateField", manager);
var nodees = new List<XmlNode>(extractNode.Cast<XmlNode>());
var templateFields = nodees.Cast<XmlNode>().Select(x => new T() //not anonymous type but T object
{
field = x.Attributes["userName"].Value,
fieldName = (string)x.Attributes["name"].Value.Substring(0, x.Attributes["name"].Value.IndexOf(':')),
type = x.Attributes["name"].Value.Substring(x.Attributes["name"].Value.IndexOf(':') + 1, 4)
}).ToList();
return templateFields;
}
Upvotes: 1