Here T could be an array or a single object. How can add the array to an arraylist or add a single object to the same arraylist. This gives me a build-time error that the overloaded match for AddRange has invalid arguments.
T loadedContent;
if (typeof(T).IsArray)
{
contentArrayList.AddRange(loadedContent);
}
else
{
contentArrayList.Add(loadedContent);
}
EDIT: Corrected my answer after checking some of the rules around casting to type Array.
All specific types of arrays, such as int[], string[], or MyCustomObject[] derive from the base Array class, and as such, they implement the ICollection interface, which is what the ArrayList.AddRange method accepts as a parameter.
Assuming that your contentArrayList variable is an ArrayList object, you should be able to cast your loadedContent variable to ICollection:
contentArrayList.AddRange((ICollection)loadedContent)
Alternatively, you could combine the check for whether it is an array with the cast:
Array loadedContentAsArray = loadedContent as Array;
if (loadedContentAsArray != null)
{
contentArrayList.AddRange(loadedContentAsArray);
}
The solution provided by Dr. Wily's Apprentice will work but I would like to make some side comments.
You must have design issue in your code if you use generics but you are still committed to a specific data type. Basically you destroy the purpose of the generics, as stated on MSDN:
Generics allow you to define type-safe data structures, without committing to actual data types.
Maybe you should reconsider some refactoring, maybe by adding methods with different parameters or something...
If your contentList is of Type ArrayList, I would go that way.
ICollection contentArray = loadedContent as ICollection;
if (contentArray != null)
contentList.AddRange(contentArray);
else
contentList.Add(loadedContent);
Related
Can someone explain me this code, especially I'm not sure how generic function as parameter works:
result.Notes= orderInfo.Notes.SafeConvert(x => (Communication.OrderNotes)x);
public static TOut[] SafeConvert<TIn, TOut>(
this TIn[] input,
Func<TIn, TOut> converter)
{
if (input == null)
{
return null;
}
return input
.Where(i => i != null)
.Select(converter)
.ToArray();
}
SafeConvert is a generic extension method. The first parameter (an array of the generic type TIn) is implicitly added when the method is invoked on an array of some type (in this case maybe a note?). The method also requires an function as a parameter. This function must take an instance of the type TIn and return a TOut instance. So, you'd invoke this method on an array of some type, supply a lambda expression or a delegate function, and it will return an array of whatever type your supplied function returns. It does this by using Linq to filter out nulls, run each item in the array through the method, then return the enumeration of those items as an array.
In the implementation you've given, it takes the "Notes" of "orderInfo" and explicitly casts them to "CommunicationOrderNotes."
Here's another way you could invoke the method.
var decimals = new [] {5, 3, 2, 1}.SafeConvert(someInt => (decimal) someInt);
This is what's known as an extension method. It's a static function that allows you to "add" methods to types without modifying the original code. It's somewhat analogous to the Decorator Pattern but there's controversy about whether it's actually an implementation of that particular pattern.
"Under the hood," at least, extension methods are just "syntactic sugar" for calling a static method, but you can call them as if they were an instance method of the extended object (in this case, arrays).
The <TIn, TOut> part means that TIn and TOut are some type you haven't specified yet (but that you intend to specify what they actually are when you go to use the class). To understand the purpose of this, think of a Linked List - really, the implementation of a Linked List of integers isn't any different than the code for a Linked List of strings, so you'd like it to be the case that you can create a single class and specify later that you want a list of integers or a list of strings or whatever. You definitely would not want to have to create an implementation for every single possible type of object - that would require a massive amount of redundant code.
Now, for the LINQ query:
return input
.Where(i => i != null)
.Select(converter)
.ToArray();
LINQ (Language Integrated Query) is a mechanism for querying different types of collections using a single syntax. You can use it to query .NET collections (like they're doing here), XML documents, or databases, for example.
LINQ Queries take an anonymous function of some kind and apply the operator to the collection in some way (see below).
Going through this query;
.Where(i => i != null)
As the name suggests, Where applies a filter. When applied to a collection, it returns a second collection with all of the elements of the first collection that match the filter condition. The
i => i != null
bit is the anonymous function that acts as a filter. Basically, what this is saying is "give me a collection with all of the members of the array that aren't null."
The Select method applies a transform to every element of the collection and returns the result as a second collection. In this case, you apply whatever transformation you passed in as an argument to the method.
It might sound slightly odd to think of code as data, but this is actually very routine in some other languages like F#, Lisp, and Scala. ("Under the hood", C# is implementing this behavior in an object-oriented way, but the effect is the same).
The basic idea of this function, then, is that it converts an array of one type to an array of a second type, filtering out all of the null references.
I'm using a service where I don't know the actual structure of the response. Therefore I'm returning it as an object:
object result = Service.GetStuff();
If I inspect it in the debugger, it looks something like this:
I've tried casting it to dynamic, Hashtable and Arraylist, without success. How do I access the properties on the object? Similar SO questions haven't helped me.
Cannot apply indexing with [] to an expression of type `object'
If the result is always an array (or something that implements IList, like an ArrayList), you can cast it to an IList, which is where the index operator is defined:
IList result = Service.GetStuff() as IList;
Then you can apply the index operator. If it's not always an array then you could use reflection to determine if it is an array, then cast it.
The bigger question is, what are you going to do with the objects since you don't know what they are?
It seems that it is some sort of dictionary try casting the object to IDictionary. If that does not help, you may call the result.GetType() to see what is the actual type and cast to that.
I also think that dynamic should work too.
dynamic result = Service.GetStuff();
int reconnectDelay = ((dynamic)result[0]).reconnectDelay;
Consider this code:
static void Main(string[] args)
{
var ints=new List<int> {10,11,22};
Something(ints);//Output:Count is:3
Something(new int[10]); //'System.Array' does not contain
// a definition for 'Count'
Console.ReadLine();
}
static void Something(ICollection collection)
{
dynamic dynamic = collection;
Console.WriteLine("Count is:{0}", dynamic.Count);
}
When pass a List all thing is ok. But when pass array and convert to dynamic i get this error:'System.Array' does not contain a definition for 'Count'.
I know what is my solution but i want to know why compiler has a this behaviors?
Something(new int[10]);
static void Something(ICollection collection)
{
//The dynamic keyword tells the compilier to look at the underlying information
//at runtime to determine the variable's type. In this case, the underlying
//information suggests that dynamic should be an array because the variable you
//passed in is an array. Then it'll try to call Array.Count.
dynamic dynamic = collection;
Console.WriteLine("Count is:{0}", dynamic.Count);
//If you check the type of variable, you'll see that it is an ICollection because
//that's what type this function expected. Then this code will try to call
//ICollection.Count
var variable = collection;
Console.WriteLine("Count is:{0}", variable.Count);
}
Now that we can understand why dynamic.Count is trying to call System.Array.Count. However, it's still unclear why Array.Count is not defined when Array implements System.Collections.ICollection which has a Count method. Array does in fact implement ICollection correctly, and it does have a Count method. However, consumers of Array.Count do not have permission to access the Count property without explicitly casting the Array to an ICollection. Array.Count is implemented with a pattern known as explicit interface implementation where Array.Count is explicitly implemented for ICollection. And you may only access the count method by casting your variable to an ICollection with this pattern. This is reflected in the docs for Array. Look for the "Explicit Interface Implementations" section.
var myArray = new int[10];
//Won't work because Array.Count is implemented with explicit interface implementation
//where the interface is ICollection
Console.Write(myArray.Count);
//Will work because we've casted the Array to an ICollection
Console.Write(((ICollection)myArray).Count);
Dynamic works internally using reflection. The array class does not have a property Count. It has a property Length which explicitly implements the ICollection property Count. This means that when you try to do the dynamic invocation, it fails, because it can not find a matching property.
My question for you would be why are you trying to use dynamic in this case -- you've already limited it to classes that support an interface, at that point you should be using the interface (which would work). At this point you're pretty much guaranteed to be able to get an enumerator and the count -- nothing else. If you need more, consider a better interface.
"I know what is my solution but i want to know why compiler has a this behaviors?"
For your question, as I know the compiler behavior is..
The compiler doesn't handle the dynamic type variable at compile-time, because the dynamic type is handled at the run-time. That's why that error appears.
If you want the compiler to handle this case, you can change your dynamic type into var type.
in a short words.. the dynamic type variable is not the compiler's responsibility.
It's because the name of the property is not Count, but rather System.Collections.ICollection.get_Count.
If you run
foreach (var item in typeof(int[]).GetMembers(BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance))
Console.WriteLine(item.Name);
you will get back
...
System.Collections.ICollection.get_Count
...
which is because the interface is implemented explicitly.
First let me say, that what I want to do is get the value of a property in a generic class that may be overriden by class that inherits from it. Think of it in the base class as a default value, that the inheritor of the class can override to set their own Default value.
I have tried to use reflection directly on the type, using the System.Reflection.FieldInfo.GetValue but this does not work for classes with generic types. So I think that I need to instantiate the class to be able to see what the value is.
The "types" I have I retrieved by reading the Dlls in the bin and using Reflection to find the types that inherit from my interface.
I am using .NET 4.5
here is documentation that seems like it explains exactly what I need to do
http://msdn.microsoft.com/en-us/library/b8ytshk6.aspx
In this documentation the only difference I can see is how we got our types, I looked in the bin for types and they simply called typeof(), Since types are so complex it seems this may be a likely mis-match but I cannot see what is missing(if anything)
foreach (var item in types)
{
var ts = item.GetField("DefaultTimeToExpire");
Type[] typeArguments = item.GetGenericArguments();
if (ts != null)
{
var t = item.MakeGenericType(typeArguments);
var obj = Activator.CreateInstance(t);
var timespan = obj.DefaultTimeToExpire;
subscriberInfos.Add(new Tuple<string, Type, TimeSpan>(item.Name, item, timespan));
}
}
I am calling GetField to look for Items that have a field "DefaultTimeToExpire" so far this part works well to find the type I need.
Next I call GetGenericArguments which returns an expected array of the type Arguments.
then I call MakeGenericType
and finally Create instance wich gives me the error message
"Cannot create an instance of BusinessLogic.TestSubscriberXXX`1[Message] because Type.ContainsGenericParameters is true."
This looks like exactly what I am supposed to do.
Thanks
In order to instantiate a generic type, you need to know the actual values (types) that should be substituted for its type parameters. The GetGenericArguments() method, being a form of reflection, only gives you the type arguments, not their actual values. The values are up to you... that is the entire point of generics.
If item is a type like List<T> then item.GetGenericArguments() will return an array containing a fake "type" representing the type parameter T (with its IsGenericParameter property set to true). Therefore, passing that parameter type back into item.MakeGenericType() will simply create another open generic type equivalent to the original. To close the generic type so that it can be instantiated you need to provide an actual (non-parameter) type argument, such as int.
For example, typeof(List<>).MakeGenericType(typeof(int)) will return typeof(List<int>), while typeof(List<>).MakeGenericType(typeof(List<>).GetGenericArguments()) will simply return typeof(List<>) again. This is what is happening in your code.
I'm sorry if that is a bit opaque, I don't know how else to explain it. The bottom line is that a type like List<T> is only useful if you have a type you want to substitute in place of T.
I have tried many ways like
Cast<CustomObject>, as Customobject and ToArray(Customobject) but nothing worked.
How can I add List or ArrayList via AddRange to a CustomObject[] Array?
Code is really difficult.
But if you have some time you can get the complete source of the destination list from here:
http://www.codeproject.com/Articles/4012/C-List-View-v1-3?msg=3844172#xx3844172xx
This is a Custom Listview
I activated a combobox for the second column, so I can select diferrent values for a cell.
But before this, I have to add something to select.
This is the hole problem.
Update:
Firstly, thanks for the help !
Secondly, Found a solution in the comments from the website with the source.
Had to add some code and changed the destination custom array to a List
list.Cast<CustomObject>().ToArray()
Will work as long as the things in the list are actually CustomObject. If they might be other types, you can use OfType<CustomObject>() instead of Cast. This will filter out anything of an incompatible type.
Assuming the objects really are instances of CustomObject, use LINQ Select method:
objList.Select(o => o as CustomObject).ToArray();
Otherwise you will get an array of null.
If its a List<CustomObject> then let us say
CustomObject[] coarr = list_of_customobject.ToArray();
If its an ArrayList then
CustomObject[] coarr = arraylist.OfType<CustomObject>().ToArray();
If you are unsure whether all of your objects are of the type CustomObject try
var result = list.OfType<CustomObject>.ToArray();
Strictly speaking you cannot add elements to an array, since an array's length remains constant over its lifetime. There are two things you can do:
Create a new array
myArray = myTList.ToArray() // generic)
myArray = myArrayList.Cast<CustomObject>().ToArray() // cast, non-generic
myArray = myArrayList.OfType<CustomObject>().ToArray() // filter by type, non-generic
Set elements of an array
myArray[x] = myTList[y] // generic
myArray[x] = (CustomObject)myArrayList[y] // non-generic
I recommend you to take the generic collection whenever possible. They provide you additional type safety. Casting object variables cause runtime errors you could detect at compile time by using generic types.
If you actually want to add elements to an existing collection, you may try to use a dynamic collection type rather than an array: List<T> : IList<T> or LinkedList<T> : ICollection<T> are a good point to start, or maybe more specific types like Stack<T> or Queue<T>.