Run member function of an object contained in an arraylist? - c#

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>.

Related

How Can I Retrieve the Underlying List of an IEnumerable Without Creating a New List?

When using IEnumerable I'm trying to avoid multiple enumerations. I know I can just use LINQ's .ToList() and be done with it, but that can be a lot of unnecessary list creation. I'd like to:
check and see if the underlying type is a List, and if so return that instance, otherwise
.ToList() it and return the new List
My thought was to use something akin to:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo(IEnumerable<T> enumerable)
{
var list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}
... but it appears from the documentation that the as operator just performs a cast, which would create a new List rather than returning the underlying one, would it not?
If so, how can I retrieve the underlying list instead of creating a new one?
The as operator does not create a new list. It only checks type and perform cast if type is compatible.
The code in the post is logically correct and matches how many LINQ methods are implemented (for example see source of Enumerable.Count which casts to ICollection to see if it can skip enumeration of items).
Note that it is important to cast to correct generic version of list or maybe one of its interfaces - IList would work if you must use non-generic version. Beware of the fact that List<T> is not co/contra-variant and type must match exactly unlike in case of covariant IEnumerable<out T> where you can cast parameter to IEnumerable<TBase> if IEnumerable<TDerived> passed.
Maybe you wanted to do this:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo<T>(IEnumerable<T> enumerable)
{
List<T> list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}

C# Get members of Generic object

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
}

C# Accessing non-interfaced methods of an object in an interface array

I have an Array of an interface named iBlocks which contains objects of more than a single class(that all implement the iBlocks interface). I'm wondering if it is possible, or how else to handle the situation in which i need to call methods not covered by the interface for all objects of a certain class within this array.
For example:
iBlocks = new iBlocks[1];
iBlocks[0] = new greenBlock();
iBlocks[1] = new yellowBlock();
foreach (greenBlock in iBlocks)
{
greenBlock.doStuff()
}
Where doStuff() is a method not defined in the interface, as it has no use in the yellowBlock class. The actual interface works brilliantly as greenBlock and yellowBlock have tons of common features. However, there are special aspects of each class i would like to still access without creating an entirely separate array for each object type.
Thanks in advance!
You can use the as operator.
foreach (var block in iBlocks)
{
var green = block as greenBlock;
if (green != null)
green.doStuff()
}
Or in LINQ
foreach (var green in iBlocks.OfType<greenBlock>())
{
green.doStuff()
}

Convert IList<T1> to IList<BaseT1>

As the easiest way to convert the IList<T1> to IList<BaseT1>?
IList<T1>.Count() is very large number!!!
class BaseT1 { };
class T1 : BaseT1
{
static public IList<BaseT1> convert(IList<T1> p)
{
IList<BaseT1> result = new List<BaseT1>();
foreach (BaseT1 baseT1 in p)
result.Add(baseT1);
return result;
}
}
You'll get much better performance in your implementation if you specify the size of the result list when it is initalized, and call the Add method on List<T> directly:
List<BaseT1> result = new List<BaseT1>(p.Count);
that way, it isn't resizing lots of arrays when new items get added. That should yield an order-of-magnitude speedup.
Alternatively, you could code a wrapper class that implements IList<BaseT1> and takes an IList<T1> in the constructor.
linq?
var baseList = derivedList.Cast<TBase>();
Edit:
Cast returns an IEnumerable, do you need it in a List? List can be an expensive class to deal with
IList<T1>.Count() is very large number!!!
Yes, which means that no matter what syntax sugar you use, the conversion is going to require O(n) time and O(n) storage. You cannot cast the list to avoid re-creating it. If that was possible, client code could add an element of BaseT1 to the list, violating the promise that list only contains objects that are compatible with T1.
The only way to get ahead is to return an interface type that cannot change the list. Which would be IEnumerable<BaseT1> in this case. Allowing you to iterate the list, nothing else. That conversion is automatic in .NET 4.0 thanks to its support for covariance. You'll have to write a little glue code in earlier versions:
public static IEnumerable<BaseT1> enumerate(IList<T1> p) {
foreach (BaseT1 item in p) yield return item;
}

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