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.
Related
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();
I have an object that can be a list of different enum types or non-enum types (normal class instances).
To verify if object is a list is easy:
myobject is IList
Now I can verify the type of list's elements:
(myobject as IList)[0].GetType().IsEnum
But how to verify if my list's elements are enums IF I have no elements yet ?
if (myobject is IList)
{
if ((myobject as IList).Count > 0)
{
if ((myobject as IList)[0].GetType().IsEnum)
return true;
}
else
{
// how to check if the list is enum list here ?
}
}
An IList can contain whatever type it wants, so if you don't have any contents you have no way of checking. If you actually have a generic type to start with you can check by using the GetGenericArguments method of Type. (GetInterface added in case you have something that's implementing IList but doesn't have the IList type as it's first generic argument).
myobject.GetType().GetInterface("System.Collections.Generic.IList").GetGenericArguments()[0].IsEnum
You can look at the the indexer's PropertyType via Type.GetProperty:
List<int> list = new List<int>(); // an empty List<T>
Type type = list.GetType().GetProperty("Item").PropertyType; // System.Int32
bool isEnum = type.IsEnum; // of course false
List<DayOfWeek> days = new List<DayOfWeek>();
type = days.GetType().GetProperty("Item").PropertyType;
isEnum = type.IsEnum; // true
demo: http://ideone.com/3JyEf
Having just IList you can't do that - IList does not gurantee types of objects inside of it and does not let you know type of objects it would accept.
Consider uisng generic veriosn IList<T> if possible - you'll be able to get type without elements in the list.
Unless your list is a generic list you cannot, since a non generic list may contain any object.
If list is generic then inspect generic type parameters for enum types.
If list is not generic try to resolve item type by inspecting parameters of Add, IndexOf or indexer methods. It is a very ugly way to do it, but may give you a clue since many old implementations inherits List object and adds an Add overload, or some new and lazy implementations may be used to hide generic parameters like public class MyObjectList: List<MyObject> {}.
The solution everyone is proposing:
IList<Days> list = new List<Days>();
if (list is IList<Days>)
{
Console.WriteLine("list has days");
}
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!!!
Using reflection I have an object which I need to cast into an iterable list of items (type unknown, will be object). Using the Watch window I can see my object is an array of some type as it tells me the number of elements and I can explode the tree view to see the elements themselves.
Firstly, I need to check that the object passed is some kind of array (might be List, might be object[], etc). Then I need to iterate through that array. However, I can't do the type conversion.
Here's how I'm using it (abbreviated):
private static void Example(object instance, PropertyInfo propInfo)
{
object anArray = propInfo.GetValue(instance, null);
ArrayList myList = anArray as ArrayList;
foreach (object element in myList)
{
// etc
}
}
I've tried various different casts. The above doesn't raise an exception but mylist is null when anArray actually exists and contains items. The actual instance being saved is a strongly-typed List<> but could take a limited subset of forms if necessary. But the point of the exercise is that this Example() method doesn't know the basic type of the property.
Casting it to an ArrayList is only going to work if the object actually is an ArrayList. It wont work with a System.Array, or a System.Collections.Generic.List`1 for example.
I think what you actually should do is cast it to IEnumerable, since that is your only requirement to loop over it...
object anArray = propInfo.GetValue(instance, null);
IEnumerable enumerable = anArray as IEnumerable;
if (enumerable != null)
{
foreach(object element in enumerable)
{
// etc...
}
}
Try to cast to IEnumerable. This is the most basic interface all enumerables, arrays, lists etc. implement.
IEnumerable myList = anArray as IEnumerable;
if (myList != null)
{
foreach (object element in myList)
{
// ... do something
}
}
else
{
// it's not an array, list, ...
}
Simply Try This
string[] arr = ((IEnumerable)yourOjbect).Cast<object>()
.Select(x => x.ToString())
.ToArray();
Try this:
var myList = anArray as IEnumerable;
if (mylist != null)
{
foreach (var element in myList)
{
// etc
}
}
You might also need to specify the generic type of the IEnumerable, depending on your situation.
You should be able to cast it to IEnumerable if it is a collection of any sorts (array, list, etc.). Also PropertyInfo contains a PropertyType property which you could use to find out the actual type if you wanted to.
Just in My case I need to define data type IEnumerable<string>
var myList = anArray as IEnumerable<string>;
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());
}