I've spent way to much time on trying to resolve this and I don't understand why I can't cast this. I'm executing a query and pulling all the values from a specific column that is then stored in a List since it could be an int, string, or bool. The issue is during casting, I would like a dynamic solution that can validate the object type and cast accordingly.
//This handles the db connection and makes calls DbItems class
public List<object> GetDBColumns(string sqlQuery, string column)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
var reader = new SqlCommand(sqlQuery, connection).ExecuteReader();
var values = DbItems.GetColumns(reader, column);
connection.Close();
return values;
}
}
Public class DbItems
{
DbItems(SqlDataReader reader, string column)
{ //GetInt32 won't be able to handle other types of course, what could I use?
columnData.Add(reader.GetInt32(reader.GetOrdinal(column)));
}
List<object> columnData = new List<object>();
//I'm calling this static method that invokes the constructor
public static List<object> GetColumns(SqlDataReader reader, string column)
{
List<object> dataSet = new List<object>();
while (reader.Read())
{
dataSet.Add(new DbItem(reader, column).columnData);
}
return dataSet;
}
}
This works without issue, but then I'll get an int value that I'd like to cast a string and I've used Cast<>, (string)columnData[0], and a couple other suggestions online and nothing works. Please assist.
Casting List<object> to List<string> would require contravariance of List's generic parameter T. Unfortunatelly List<T> does not meet criteria for contravariance, because it is not an interface, delegate or array type, and because making it's generic parameter contravariant wouldn't be type-safe (doing so would e.g. allowed List<string> to contain not only strings, but any other object, which is obviously nonsence, not type-safe and therefore not allowed). Fot this reason, you cannot cast List<object> to List<string>.
What you can, however, is to create new List<string> and copy there all items from original List<object>, while converting each item to string. Item conversion with Cast<> or (string)columnData[0] will not work here (unless items are actually a strings), because casting a reference-type object to another reference-type only performs assignment compatibility check, but does not perform any conversion of the object.
Luckily, converting to string is trivial, since all objects inherits .ToString() method from Object type. So you can convert to List<string> with the following:
List<string> stringList = columnData.ConvertAll(item => item?.ToString());
But of course, you can use any other conversion, if .ToString() does not meet your needs, such as using Convert class to perform conversion between primitive types.
The easiest way to do so would be just call the ToString Method, since all object have a ToString Method
var values = columnData.Select(x => x?.ToString()).ToList();
Related
The method I am overriding has the following signature.
public override bool IsValid(object value)
The object that is passed in is a List but the the list type is unknown. It could be List<string> or List<int>.
I need to cast this into a List<object>. I've tried
if (!(value is IList temp))
{
return false;
}
List<object> list = temp.OfType<object>().ToList();
which sort of works, but it filters out any null values, presumably because they are not OfType<object>
So what's the best way of doing this?
temp.OfType().ToList();
OfType checks if the element is of the required type, and if so it puts it in a new temporary list.
What you're asking is simply a cast, since every instance in C# is an object: (List<object>)temp. Edit: apparently covariance doesn't work here.
There's absolutely no reason to do this though, you can simply enumerate it as it is: foreach(var obj in (IList)temp). All collections implement the IList non-generic interface.
If the input is either List<int> or List<string> and nothing else, then I think the best approach is to check specifically for these types. So something like:
List<object> list ;
if (value is List<int> listOfInts)
{
list = listOfInts.Cast<object>().ToList();
}
else if (value is List<string> listOfStrings)
{
list = listOfStrings.Cast<object>().ToList();
}
else
{
throw new ArgumentException(...);
}
This might seem redundant, but it might save you a lot of headache down the line, since you will catch if someone passes a List<SomethingElse> or even a List<object> which would be illegal according to your specification.
I know that my type is a ICollection<T>. I know that T is a primitive type, and I know that T can be turned into a string with ToString(). I have the Type object of T (By using: Type genericType = info.PropertyType.GenericTypeArguments[0];
I do not know how to get the items in that collection out and into another collection of a known type (Like a List<string>). Thankfully because all I want are the values of it's items as a string I can use ToString() on each one regardless of it's type (constrained to primitives).
I use this in another area of my program to get the values of all the properties of a class as long as they are primitive. I have tried casting the Type object as the generic argument for an ICollection<T> and that did not work.
Edit: I have an object that I know is an ICollection<T>, but I cannot seem use LINQ on it till it's casted to an ICollection<T>, I cannot cast to that because T is "unknown" (I have retrieved the Type of T but am not sure where to run with that).
Since you start with generic collection and ICollection<T> implements IEnumerable<T>, you can use LINQ:
List<string> res = coll.Select(elem => elem.ToString()).ToList();
Edit - you can cast the object to non-generic IEnumerable or ICollection, then call Cast<object> on that to get a generic collection (you can do that because you just want to call ToString on elements):
object obj = new List<int> { 1, 2, 3 };
IEnumerable res = obj as IEnumerable;
List<string> result = res.Cast<object>().Select(e => e.ToString()).ToList();
foreach (var e in result)
Console.WriteLine(e);
I know I'm missing something fundamental with either generics or covariance, and I was hoping there is a better way to do what I am doing.
I have a method that takes a list of domain objects and turns it into an HTML table:
public String GenerateTable(List<object> Data, String[] Properties,
String[] ColumnHeaders = null)
{
}
When I call the method, I find myself having to do this:
List<Customer> cust = GetCustomers();
List<object> oCust = new List<object>;
foreach (Customer c in cust)
oCust.Add((object)c);
string table = GenerateTable(oCust, new string[] { "CustNbr", "CustName" });
I believe with covariance I can simply:
List<object> oCust = cust;
But I'm looking for a better solution all-around -- eliminate the necessity to create a completely new list each time I run this method. It's not a performance or memory issue, as these lists are always relatively small, but I'd like to understand what is the best (or at least better) way to accomplish this.
You should change GenerateTable to accept an IEnumerable of objects instead of a list. Then you won't have to convert your Customer list to a list of objects.
public String GenerateTable(IEnumerable<object> Data, String[] Properties, String[] ColumnHeaders = null)
The problem with your original version is that GenerateTable could attempt to add a non-Customer object to the List. IEnumerable works because it is read only. You can read more about it here, if you are interested.
Covariance is only supported in generic interfaces. Since it looks like an IEnumerable would be sufficient you can try to not use a generic at all.
public string GenerateTable(IEnumerable data, string[] properties, string[] columnHeaders = null)
Alternately, you could set up a generic transformation method
public string GenerateRow(Customer customer) { // convert one object here}
public string GenerateTable<T>(List<T> objects, Func<T,string> rowGenerator)
{
// table boilerplate
foreach(var obj in objects) {
output.Append(rowGenerate(customer))
}
}
and then call it with
var table = GenerateTable(customerList, GenerateRow);
to generate your table.
The question maybe a little confusing, but it's hard to make clear this question in a subject title.
I have method declared and implemented like this:
public IList<string> GetBookTitles()
{
IList<string> bookTitles = new List<string>();
// do something to populate the bookTitles list.
return bookTitles;
}
Why can't I pass the result of this method to a List<string>? After all, List<string> is a kind of IList<string>.
Well, for starters, just look at the members of IList and compare it with List. List has methods that an IList doesn't. (List has a BinarySearch method that IList doesn't, just as a single example.)
Arrays also implement IList, as an example. An array however is not a List, so you can't, and shouldn't, be able to pass a string[] to a method that accepts a List<string>.
You have a few possible solutions. One would be to just change your method to return a List<string> rather than an IList<string> (that's what I'd suggest). If that's what you really need then you shouldn't be restricting the return type to IList<string>. Another (poorer) option would be to cast the result back to a List<string> before passing it to the next method, since you happen to know that it's what the underlying type really is.
After all, List<string> is a kind of IList<string>.
But there are also other kinds of IList<String>.
What if your method were to return an IList<String> which is a ReadOnlyCollection<String> instead?
IList<string> x = new ReadOnlyCollection<string>();
List<string> y = x; //Huh?
The compiler uses the signature of your methods, not the implementation when deciding if you can assign the result of GetBookTitles to your variable, so it can't know that the result will in fact be a List. If it would allow you to do such a thing, then you could write something like this:
List<string> myBooks = GetBookTitles();
myBooks.Sort();
In your example you could do this, and in fact you can if you cast the result of your method:
List<string> myBooks = (List<string>)GetBookTitles();
But then one day you could decide that your book collection is not modifiable, and you rewrite your method as follows:
public IList<string> GetBookTitles()
{
IList<string> tmp = new List<string>();
// do something to populate the bookTitles list.
IList<string> bookTitles = new ReadOnlyCollection<string>(tmp);
return bookTitles;
}
ReadOnlyCollection does not implement Sort, so your app would compile, but would crash at runtime.
Using the cast approach it would crash when trying to do the cast, but in this case you are taking the responsibility of deciding that that kind of cast is feasible and do not have the compiler trying to guess.
A better approach could be to use as instead of the cast and chek for null. I.e.:
List<string> myBooks = GetBookTitles() as List<string>;
if (myBooks != null)
myBooks.Sort();
You should be able to, you just need an explicit conversion.
List<string> foo = (List<string>)GetBookTitles()
should do it.
The interface may be implemented in various classes which are not same. So, it will be difficult to find the respective class.
You can type cast from IList to List!!!
I've created a generic function as below (just a s a proof) that will take a List<T> collection and reverse it, returning a new List<T> as its output.
public static List<T> ReverseList<T>(List<T> sourceList)
{
T[] outputArray = new T[sourceList.Count];
sourceList.CopyTo(outputArray);
return outputArray.Reverse().ToList();
}
The purpose of the proof is that I only know what T is at runtime. I am therefore using reflection to call the above method as follows:
List<int> myList = new List<int>() { 1, 2, 3, 4, 5 }; // As an example, but could be any type for T
MethodInfo myMethod = this.GetType().GetMethod("ReverseList");
MethodInfo resultMethod = myMethod.MakeGenericMethod(new Type[] { typeof(int) });
object result = resultMethod.Invoke(null, new object[] { myList });
There are two problems here:
In the second line, rather than supplying typeof(int), I would like suppliy somthign akin to myList.GetType().GetGenericArguments()[0].GetType() in order to make things more flexible because I do not know T until runtime. Doing this results in a runtime error when the Invoke runs as follows: "Object of type 'System.Collections.Generic.List'1[System.Int32]' cannot be converted to type 'System.Collections.Generic.List'1[System.RuntimeType]'."
The result of the Invoke() method returns an object. When debugging, I can see that the object is of type List, but attempting to use it tells me that I have an invalid cast. I assume that I need to use reflection to box the result in to the correct type (i.e. in this example, the equivalent of (result as List<int>).
Does anyone have any pointers that could help me resolve this? Apologies if this is not to clear, I can probably provide more detail if asked.
TIA
You've got one GetType() too many. Happens to everyone.
myList.GetType().GetGenericArguments()[0] IS a System.Type -- the one you're looking for.
myList.GetType().GetGenericArguments()[0].GetType() is a System.Type describing System.Type (well, actually the concrete subclass System.RuntimeType).
Also, your ReverseList function is serious overkill. It does an extra copy just to avoid calling List.Reverse. There's a better way to circumvent that:
public static List<T> ReverseList<T>(List<T> sourceList)
{
return Enumerable.Reverse(sourceList).ToList();
}
or
public static List<T> ReverseList<T>(List<T> sourceList)
{
var result = new List<T>(sourceList);
result.Reverse();
return result;
}
or
public static List<T> ReverseList<T>(List<T> sourceList)
{
var result = new List<T>();
result.Capacity = sourceList.Count;
int i = sourceList.Count;
while (i > 0)
result.Add(sourceList[--i]);
return result;
}
To access it as a List<T>, yes you'd need to find T using reflection (probably over the interfaces, for example typeof(IList<>), and use more reflection and MakeGenericMethod etc. In all honesty, it isn't worth it: you would do better to check for the non-generic IList:
var list = result as IList;
if (list != null)
{
// loop over list etc
}
Generics ad reflection are not good friends.
Note in 4.0 there are also some tricks you can do here with dynamic and generics.
The result of the Invoke() method
returns an object. When debugging, I
can see that the object is of type
List, but attempting to use it tells
me that I have an invalid cast. I
assume that I need to use reflection
to box the result in to the correct
type (i.e. in this example, the
equivalent of (result as List).
The only workaround for this is I can think of is to pass an empty list as the second parameter of the method and to populate that list - the reference returned by Invoke() will always be only of type object, but inside the generic method you do have access to the type itself:
List<int> reverseList = new List<int>();
resultMethod.Invoke(null, new object[] { myList, reverseList });
...
public static void ReverseList<T>(List<T> sourceList, List<T> resultList)
{
T[] outputArray = new T[sourceList.Count];
sourceList.CopyTo(outputArray);
resultList.AddRange(outputArray.Reverse());
}