C# Get members of Generic object - c#

I will highly appreciate any help. I have a string as an input. Let's call it as 'table_name'. I want to create a collection of table_names (where table_name is an actual object in a referenced project). I did the following:
Object obj = Activator.CreateInstance("ClassLibrary", "ClassLibrary." + table_name);
Type CollectionType = typeof(Collection<>).MakeGenericType(new[] { obj.GetType() });
ICollection c = (ICollection)Activator.CreateInstance(CollectionType);
Then I called a method (which is located in different project and which returns Collection of objects) to fill out my ICollection object (in this case, c):
object[] parameters = new object[] { x_coord, y_coord, buffer_dist};
c = (ICollection)sde_db.GetType().GetMethod("Method" + table_name).Invoke(sde_db, parameters);
So far everything works fine. I can see the number of elements in the collection. But the problem is when I try iterate through the collection, it doesn't show its elements. I can see them only during run time. Is there a way to retrieve members of the collection during compile time? I want something like:
for(int i = 0; i < c.Count; i++){
label.Text = c[i].Details;
}
Thanks!

Yes, but you have to answer the question of what members are there and how you plan to use them (and most importantly, how the compiler knows what you mean when you write the code referring to them, e.g. .Details). E.g. if your objects will always implement an interface...
public interface IHasDetails
{
string Details { get; }
}
Then you can cast the object to that type:
label.Text = ((IHasDetails)c[i]).Details;
If you just want to access it dynamically, use dynamic:
label.Text = ((dynamic)c[i]).Details;

ICollection does not define an index accessor so you cannot do c[i].
Can't you use an IList instead?

In order to access the members during compile time you need to be using a concrete type that has the members available. Given that your dynamically accessing the types involved this doesn't seem possible unless they have a common, and known, base type or interface. Without one of those mechanisms you are reduced to some kind of dynamic access.

Since I needed only details about each element in the ICollection, I used System.Xml.Serialization to get a string containing info on a particular element in the collection.
for (int i = 0; i < c.Count; i++)
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(c[i].GetType());
using (StringWriter writer = new StringWriter())
{
x.Serialize(writer, c[i]);
String details = writer.ToString();
}
//do here what ever you want
}

Related

Access object property in array

class Program{
static void Main(string[] args){
object[] array = new object[1];
CreateItem item = new CreateItem();
item.name = "Necklace";
item.value = 5;
array[0] = item;
Console.WriteLine(array[0].name); //This part of the code doesn't work.
//It can't find the property name.
Console.ReadLine();
}
}
public class CreateItem {
public string name;
public int value;
}
Hi there! First of all I'd like to say that I'm not very familiar with objects, so excuse any mistakes you can see in the code (Although feel free to correct them, it'd be a great way to learn).
I've been working on making a small game using C#, but I came across a problem: I can't access my object properties when I put them in an array. Does anyone know which code I should use to be able to access my object properties while they're in an array?
Thanks for reading, and once again, excuse any silly mistakes I made, I'm fairly new to working with objects.
You shouldn't use an object array when you have a strong type that you're interested in using (and you know the type already).
CreateItem[] array = new CreateItem[1];
CreateItem item = new CreateItem();
item.name = "Necklace";
item.value = 5;
array[0] = item;
Console.WriteLine(array[0].name);
Necklace will now be outputted as expected.
You should probably look at using Generics and Lists, it is a very common and a valuable concept to grasp, as is the concept of Boxing and Unboxing which Generics solves.
class Program{
static void Main(string[] args){
List<CreateItem> list = new List<CreateItem>();
CreateItem item = new CreateItem();
item.name = "Necklace";
item.value = 5;
list.Add( item );
Console.WriteLine(list[0].name); //This part of the code doesn't work.
//It can't find the property name.
Console.ReadLine();
}
}
You could cast the object to your type, i.e.:
Console.WriteLine(((CreateItem)array[0]).name);
or (more effectively)
define your array as CreateItem[] array = new CreateItem[1];
Line
object[] array = new object[1];
creates an array of elements of type Object which is the base class for all other classes in .NET.
When you do:
array[n] = item;
an implicit conversion to the base type occurs and through array[n] you can access only members of the Object type portion of the CreateItem object (like ToString() or GetType() - their overrides will be called).
If you want to access entire CreateItem object, you have to cast the reference to the base type back to the original type, by using cast operator for example:
var name = ((CreateItem)array[0]).name;
This explicit casting is error-prone, has a run-time overhead and it is a sign of the poor design. When you know the type of the collection in advance, declare the collection of that type as other answers are suggesting:
// you can use array if you know number of items in advance and that number of elements will not change
CreateItem[] array = new CreateItem[N];
// use list if number of elements might change
List<CreateItem> list = new List<CreateItem>();

Store anonymous values in a list and extract each individually

I'm trying to store information in a block of anonymous values
//holds all info
var jobs = new { newJob, numBytes, requiredTime };
then take that information and place it into a list as a single element
//puts above info into a list
joblist.Add(Convert.ToString(jobs));
Console.WriteLine(joblist[0]); //testing purposes
now what I would like to do is be able to call joblist and take the value of example numBytes at position 4.
Is this possible? Or could someone help with an alternate way of doing this? Much thanks!
Create a named class. Then you can have a list of objects of that type and manipulate that list in any way you want.
Using classes is best-practice for what you are trying to do. By default you should consider storing structured data in an object model consisting of custom classes. There is another answer here which is proposing to use dynamic - this is valid and has its place, but it is more of a last resort solution. What you want is to play to the strength of C# which are rich classes and static typing. Anonymous types are also statically typed, but as you cannot name the type you cannot declare a statically typed list to hold them. You also can't use them as return types of methods.
The "normal" thing to do in C# would be to create a class to hold the information that you want to store. For example:
public class Job
{
public string Name { get; set; }
public int NumBytes { get; set; }
public DateTime RequiredTime { get; set; }
}
Then you can add these to a list:
var jobs = new List<Job>();
var aJob = new Job();
aJob.Name = "Job 1";
aJob.NumBytes = 123;
jobs.add(aJob);
Then you can access jobs by its index in the list:
var jobNumBytes = jobs[3].NumBytes;
One thing to note about C#, when you do:
new { newJob, numBytes, requiredTime };
The compiler, at build time, just creates you a strongly typed class (just like the Job class I created above) and generates a random name for it. It infers the property names and types from the variables that you are assigning to it. The created .exe or .dll actually does contain a class definition for this type, you just can't easily get to it from other places in your code. It isn't truly "dynamic". So using that syntax is usually just a lazy way of declaring a class that you just need for a moment. Usually just inside 1 method, then you don't need it any more. Creating a named class is usually what you want to do.
Actually I don't know exactly what you mean with "now what I would like to do is be able to call joblist and remove for example numBytes at position 4."
But I guess you just want to put the objects in a list and query for numBytes and maybe remove some elements.
With dynamics you can handle dynamic objects...
var jobs = new List<dynamic>();
for (int i = 0; i < 100; i++)
{
string newJob = "Job" + i;
int numBytes = i;
TimeSpan requiredTime = new TimeSpan(0,0,i);
//holds all info
var job = new { newJob, numBytes, requiredTime };
jobs.Add(job);
}
jobs.RemoveAll(p => p.numBytes > 50);
Instead of this, I agree with the comments below your question and would create a normal class which holds the properties you need and simply put instances of that into a list. Dynamics should be used only in very rare situations, and yours doesn't sound like it is extremely special.

Run member function of an object contained in an arraylist?

Basically I have an arraylist of different types of objects that all have a Draw() function. I want to use a loop to run the draw function of each object. But C# tells me that type "object" doesn't have that function. Any idea's for a lowly n00b?
That Draw method should be defined in an interface:
public interface IDrawable
{
void Draw();
}
All your other classes (with a .Draw method) should implement this interface. And your list should be a List<IDrawable>. Then you can simply invoke the .Draw method on each item as expected:
List<IDrawable> list = new ...;
foreach (var item in list)
{
item.Draw();
}
Unless you're using .NET 1.1, you shouldn't really be using the ArrayList.
If you're talking about UI elements, they should all implement the IDrawable interface. If they're your custom type, you too should implement the IDrawable interface on those classes. You can then use a generic List<T> to store them.
List<IDrawable> objects = new List<IDrawable>();
// Fill the list
// Iterate over each object and call Draw()
foreach(IDrawable obj in objects)
{
obj.Draw();
}
They should all implement an IDrawable interface. and your arraylist will probably want to contain a list of IDrawable rather than object.
If they share a common interface you can cast to that interface.
If the number of types are known and fixed, try to cast to each of the known types with AS operator.
Otherwise you'll have to resort to reflection, something like this
for (i...){
object oneObject = arrayList[i];
Type objectType = oneObject.GetType();
objectType.GetMethod("Draw").Invoke(oneObject, new object[0]);
}
But if you need to resort to reflection that is a huge code smell and should really be avoided.
With an ArrayList all of the elements are stored as the type Object. To call a function defined on antother type you'd first need to cast the members to that type.
class MyType {
public void Draw() {
...
}
}
for (int i = 0; i < arrayList.Count; i++) {
MyType current = (MyType)arrayList[i];
current.Draw();
}
This code only works though if the objects stored in the ArrayList are convertible to MyType. If you're using .Net 2.0 or later then avoid ArrayList entirely and instead use List<MyType>. This is a strongly typed collection and avoids the need for a cast at all.
List<MyType> list = ...;
for (int i = 0; i < list.Count; i++) {
list[i].Draw();
}
If you're using C# 4 or better, you can use the dynamic type for this:
foreach (dynamic obj in arrayList)
obj.Draw();
This will fail at runtime if you have an object whose type lacks a matching Draw() signature.
If you're using an earlier version of C#, you can use reflection.
Having said that, you really ought not to be using ArrayList unless you're forced to by some legacy library. The more idiomatic solution, as others suggest, is to have an IDrawable interface and a List<IDrawable>.

Cannot add items to an IList/List being casted from an IEnumerable

I'm trying to create a small proof-of-concept application for my boss, however the code I've created that simulates what he's trying to pull off isn't working.
for (int i = 0; i < 5000; i++)
{
((IList<string>) obj2.Stuff).Add("Iteration " + i.ToString());
}
I'm trying to pull this off all in one line because this is what his code looked like the other day in the framework we're working on. Anywho when the code above executes, I get a runtime error saying "Collection was of a fixed-size". And when I try casting to a List instead of an IList, I get an InvalidCastException saying "Unable to cast object of type 'System.String[]' to type 'System.Collections.Generic.List`1[System.String]'."
Anybody have any ideas on how I can pull off a single-line cast to add an item to the IEnumerable or help me figure out a way around the two errors I keep getting? Thanks in advance.
EDIT (4/19/2011 10:49AM EST)
I'm adding more code to help people out -- probably should've done this earlier. Sorry.
Program.cs:
#region Cast Test
Class1 obj2 = new Class1();
obj2.Stuff = Enumerable.Empty<string>();
Console.WriteLine("Cast - Start Time: " + 0 + "ms");
Stopwatch stopwatch2 = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 5000; i++)
{
((IList<string>) obj2.Stuff).Add("Iteration " + i.ToString());
}
stopwatch2.Stop();
Console.WriteLine("Cast - Stop Time: " + stopwatch2.ElapsedMilliseconds.ToString() + "ms");
#endregion
Class1.cs:
public class Class1
{
private IEnumerable<string> stuff;
public IEnumerable<string> Stuff
{
get { return stuff; }
set { stuff = value; }
}
}
Arrays in C# are of fixed size. You can't add items to them.
You cannot cast a string[] to IList<string> because string[] does not implement that interface. You will need to create an object implementing IList<string> first:
List<string> list = obj2.Stuff.ToList();
for (int i = 0; i < 5000; i++)
{
list.Add("Iteration " + i.ToString());
}
Why cast inside the for loop? While every IList is an IEnumerable, not every IEnumerable is an IList. You can use the ToList extention method to copy the IEnumerable to a new List. (Just be sure to be using System.Linq;)
Also, in this example, there is no need for a for loop:
var list = obj2.Stuff.ToList();
new list.AddRange(Enumerable.Range(0, 5000).Select(i => "Iteration " + i.ToString()));
In comments on one of the answers above you say "I'm trying to avoid copying the list."
You are trying to add data to an object whose underlying type is IEnumerable<T>. But an IEnumerable object is not an actual container, it's an interface. You can't add stuff to an interface. But you can assign another object that implements that interface to it.
So to use foson's example above, you could just do:
obj2.Stuff = Enumerable.Range(0, 5000).Select(i => "Iteration " + i.ToString());
Note that when this code executes, nothing actually happens. No objects will be created until something actually iterates over obj2.Stuff. When that happens, the methods in LINQ will be called that create objects one at a time and return them to the iterator loop.
But there's no actual storage device involved here. You can iterate over obj2.Stuff and unless you consequently added each integer to something else, they would be gone at the next iteration.
The fundamental point here is you can't store things in IEnumerable, rather, IEnumerable is a way to return objects in sequence, and that could be from a list construct, or from a function that generates a sequence.
You can't call Add since obj2.Stuff seems to be an array, and an array has a fixed size, as the error message indicates.
The type System.String[] is an array of strings, which implements IEnumerable<string> but is not a List<string>. Arrays are of fixed length. Why do you need it in one line?
Change the type of obj2.Stuff to something like StringCollection or List . In .NET arrays are of fixed length and you can't add new objects to them, so use collections instead.
An IEnumerable does not give you the same features that an IList. Unless your Stuff is an IEnumerable that is also an IList your code won't work.
The contract of the first only promises that you can iterate it one element at a time. It never states that you can add new elements.
You can only pull this off if and only if at runtime your enumerable object also satisfies the IList interface.
As obj2.Stuff is a string array, it's not possible to do what you are trying to do. An array can't be resized, so you can't add items to it.
If you can replace the array in obj2.Stuff with another array, you can get the data from the array and make a list from it, add items to the list, get the data from the list and create an array from it, and use that to replace the original array:
List<string> items = new List<string>(obj2.Stuff);
for (int i = 0; i < 5000; i++) {
items.Add("Iteration " + i.ToString());
}
obj2.Stuff = items.ToArray();
If the obj2.Stuff property is read-only, then you can't add anything to it unless you change then underlying implementation from an array to a collection that you can add items to.

ReadOnlyCollection or IEnumerable for exposing member collections?

Is there any reason to expose an internal collection as a ReadOnlyCollection rather than an IEnumerable if the calling code only iterates over the collection?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
As I see it IEnumerable is a subset of the interface of ReadOnlyCollection and it does not allow the user to modify the collection. So if the IEnumberable interface is enough then that is the one to use. Is that a proper way of reasoning about it or am I missing something?
Thanks /Erik
More modern solution
Unless you need the internal collection to be mutable, you could use the System.Collections.Immutable package, change your field type to be an immutable collection, and then expose that directly - assuming Foo itself is immutable, of course.
Updated answer to address the question more directly
Is there any reason to expose an internal collection as a ReadOnlyCollection rather than an IEnumerable if the calling code only iterates over the collection?
It depends on how much you trust the calling code. If you're in complete control over everything that will ever call this member and you guarantee that no code will ever use:
ICollection<Foo> evil = (ICollection<Foo>) bar.Foos;
evil.Add(...);
then sure, no harm will be done if you just return the collection directly. I generally try to be a bit more paranoid than that though.
Likewise, as you say: if you only need IEnumerable<T>, then why tie yourself to anything stronger?
Original answer
If you're using .NET 3.5, you can avoid making a copy and avoid the simple cast by using a simple call to Skip:
public IEnumerable<Foo> Foos {
get { return foos.Skip(0); }
}
(There are plenty of other options for wrapping trivially - the nice thing about Skip over Select/Where is that there's no delegate to execute pointlessly for each iteration.)
If you're not using .NET 3.5 you can write a very simple wrapper to do the same thing:
public static IEnumerable<T> Wrapper<T>(IEnumerable<T> source)
{
foreach (T element in source)
{
yield return element;
}
}
If you only need to iterate through the collection:
foreach (Foo f in bar.Foos)
then returning IEnumerable is enough.
If you need random access to items:
Foo f = bar.Foos[17];
then wrap it in ReadOnlyCollection.
If you do this then there's nothing stopping your callers casting the IEnumerable back to ICollection and then modifying it. ReadOnlyCollection removes this possibility, although it's still possible to access the underlying writable collection via reflection. If the collection is small then a safe and easy way to get around this problem is to return a copy instead.
I avoid using ReadOnlyCollection as much as possible, it is actually considerably slower than just using a normal List.
See this example:
List<int> intList = new List<int>();
//Use a ReadOnlyCollection around the List
System.Collections.ObjectModel.ReadOnlyCollection<int> mValue = new System.Collections.ObjectModel.ReadOnlyCollection<int>(intList);
for (int i = 0; i < 100000000; i++)
{
intList.Add(i);
}
long result = 0;
//Use normal foreach on the ReadOnlyCollection
TimeSpan lStart = new TimeSpan(System.DateTime.Now.Ticks);
foreach (int i in mValue)
result += i;
TimeSpan lEnd = new TimeSpan(System.DateTime.Now.Ticks);
MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
MessageBox.Show("Result: " + result.ToString());
//use <list>.ForEach
lStart = new TimeSpan(System.DateTime.Now.Ticks);
result = 0;
intList.ForEach(delegate(int i) { result += i; });
lEnd = new TimeSpan(System.DateTime.Now.Ticks);
MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
MessageBox.Show("Result: " + result.ToString());
Sometimes you may want to use an interface, perhaps because you want to mock the collection during unit testing. Please see my blog entry for adding your own interface to ReadonlyCollection by using an adapter.

Categories