I want to create a method that is able to add an item to a collection whose type is unknown at compile time. It will receive two objects: the collection and the item.
Currently, I have this:
public void Add(object collection, object item)
{
var list = (IList)collection;
list.Add(item);
}
The problem? It doesn't work when the collection is a, for example, an instance of this type:
public sealed class ColumnDefinitionCollection : IList<ColumnDefinition>, IEnumerable<ColumnDefinition>
{
}
What can I do to make it work with every kind of instance that has an Add method?
EDIT: I'm tried with the method proposed by #lukegv, but I'm getting this when the collection is a ColumnDefinitionCollection (running in a Universal Windows Application):
I created another question related to this issue ("Add" not being retrieved) Unable to get the "Add" method of a ColumnDefinitionCollection in UWP
I tried to extend Zdravko Danevs answer by some checks to prevent undesired behavior like the one you and Jon Skeet mentioned (DateTime.Add).
This one checks whether the type expected by the collections Add-method equals the type provided by the item.
static void AddToCollection(object collection, object item)
{
MethodInfo addMethod = collection.GetType().GetMethod("Add");
if (addMethod == null || addMethod.GetParameters().Length != 1)
{
// handle your error
return;
}
ParameterInfo parameter = addMethod.GetParameters().First();
if (parameter.ParameterType.Equals(item.GetType()))
{
addMethod.Invoke(collection, new object[] { item });
}
else
{
// handle your error
}
}
While this is probably not what you want to do, you can use reflection to check if there is an Add method and call it with the parameter supplied. Something like this (untested):
Type t = collection.GetType();
MethodInfo add = t.GetMethod("Add");
if (add != null)
{
params = new object[] { item };
add.Invoke(collection, params);
}
Related
So I got the following class...
public partial class CommandBar : UserControl { .. }
...which i include in the .ascx file like so...
<%# Register Src="~/.../CommandBar.ascx" TagName="CommandBar" TagPrefix="uc1" %>
...
<uc1:CommandBar ID="CommandBarTop" runat="server" ... />
My goal at the moment is to create a generic method which allows a user to set all controls to readonly recursively. The method should provide an optional parameter to specify a List<Type> of controls to ignore. In this list I want to pass this CommandBar with typeof(CommandBar) to ignore those.
Everything works like expected but I'm having a little trouble figguring out the proper way to match these types.
Consider the following;
Object o; // control to check, in this case the `CommandBarTop` object
Type t; // type to ignore
I expected it to be as easy like this:
if (o is t){
// ignore
}
... but I get a syntax exception "A constant value is expected". So i tried it with the following setup:
if (t == typeof(o)){
// ignore
}
It did compile but did not work like expected. The problem seems to be a type missmatch. Taking a look at the debugger I get the following:
t => {Name = "CommandBar" FullName = "My.Name.Space.Controls.CommandBar"} System.Type {System.RuntimeType}
o => {ASP.controls_commandbar_ascx} object {ASP.controls_commandbar_ascx}
o.base is actually of type t but first of all it is not accessible and second the method should be generic, checking a base type to match would probably not always do what I want.
I assume that ASP.NET generates a control wrapper at runtime which then gets sent to the user. This assumption is based on the assembly codebase I see in the debugger. It says the following:
t.Assembly.CodeBase => "file:///.../bin/My.Name.Space.Project.DLL" string
o.GetType().Assembly.CodeBase => "file:///C:/Windows/Microsoft.NET/Framework/.../Temporary ASP.NET Files/root/.../App_Web_....DLL" string
I also tried matching the type GUID but since they are basically not the same type that doesn't work either.
EDIT 1
I thought it might help if I show you my method to set the controls to readonly recursively
public static void SetControlRecursivelyReadOnly(Object control, Boolean readOnly, IEnumerable<Type> controlTypesToIgnore = null)
{
if (null == control)
{
return;
}
new List<KeyValuePair<Type, String>>
{
// define all types which are relevant to access possible child controls
new KeyValuePair<Type, String>(typeof(ControlCollection), "Controls"),
new KeyValuePair<Type, String>(typeof(TableRow), "Rows"),
new KeyValuePair<Type, String>(typeof(TableCell), "Cells")
}.ForEach(x =>
{
// get defined property
Object property = typeof(Reflection).GetMethod("GetProperty")
.MakeGenericMethod(x.Key)
.Invoke(null,
new[]
{
control,
x.Value
});
// check if property is found and is IENumerable
if (!(property is IEnumerable))
{
return; // continues the foreach loop
}
// call recursive
foreach (Object o in (IEnumerable) property)
{
// <--- TODO CHECK IF CONTROL TYPE SHOULD BE IGNORED --->
SetControlRecursivelyReadOnly(o, readOnly);
}
});
// set relevant properties accordingly to readOnly parameter
new List<Tuple<PropertyInfo, Boolean>>
{
new Tuple<PropertyInfo, Boolean>(control.GetType().GetProperty("ReadOnly"), readOnly),
new Tuple<PropertyInfo, Boolean>(control.GetType().GetProperty("EnableButtons"), !readOnly),
new Tuple<PropertyInfo, Boolean>(control.GetType().GetProperty("Enabled"), !readOnly)
}.Where(x => null != x.Item1)
.ToList()
.ForEach(x => x.Item1.SetValue(control, x.Item2, null));
}
Coming to my question now; anyone got an idea how to solve this problem?
Thanks in advance!
Try with the name prop of your type. Something like below should work
if (typeof(o).Name == t.GetType().Name)
{
// ignore
}
You need to loop recursively all Controls in your page.
public void FindChildControlsRecursive(Control control)
{
foreach (Control childControl in control.Controls)
{
// add .BaseType in the next line if it's a UserControl
if (childControl.GetType() == typeof(T))
{
_foundControls.Add((T)childControl);
}
else
{
FindChildControlsRecursive(childControl);
}
}
}
private readonly List<T> _foundControls = new List<T>();
public IEnumerable<T> FoundControls
{
get { return _foundControls; }
}
In the same way you can set a property containing controls to exclude.
You can check: How to get the "typeof" of a custom user control
Is it possible to check if the list contains an object of given (but dynamic) type, derrived from same basic abstract class?
The main problem is not about the list, but about comparing types itself.
In single variables and static variables, it's easy:
if(someVariable is int)
Checking the list with static type is also easy, like:
SomeList.OfType<int>().Any()
or
(from _Object in SomeList.OfType<int> where _Object is int select _Object).Count() == 0
but I cant't handle it if the type I want to check is dynamic, f.e. passed as method parameter:
abstract class BasicClass;
class DerivativeOne : BasicClass { }
class DerivativeTwo : BasicClass { }
// in main:
List<BasicClass> _List = new List<BasicClass>();
DerivativeOne a = new DerivativeOne();
DerivativeTwo b = new DerivativeTwo();
DerivativeOne c = new DerivativeOne();
if(!CheckIfTypeExistsInList(a, _List)
{
_List.Add(a);
}
if(!CheckIfTypeExistsInList(b, _List)
{
_List.Add(b);
}
if(!CheckIfTypeExistsInList(c, _List)
{
_List.Add(c); // this is what I don't want to happen,
// because I already have one object of type DerivativeOne in my list.
}
// the function:
bool CheckIfTypeExistsInList(BasicClass pObject, List<BasicClass> pList)
{
/// few attempts:
pList.OfType<(pObject.GetType()>().Any(); // attempt one, error
return (from _Object in SomeList.OfType<(pObject.GetType())> where _Object is int select _Object).Count() == 0; // attempt two, error
}
PS. I am aware that the code doesn't look neat, but I tried to show just the problem itself, skipping extra logic and stuff.
PS2. I am aware that the solution to the problem would be just to put some attribute to BasicClass and make each derivative to have unique value of the attribute, but still - I'm not looking for another route to solve the problem, I'm just interested if it's possible to do it "this" way.
When the type is known only at runtime, you cannot use it in a generic without using reflection. However, your task is simpler than that - you can use type equality to achieve the results that you want:
Type targetType = pObject.GetType();
if (SomeList.Any(o => targetType.Equals(o.GetType()))) {
...
}
Hi I'm not sure a generic method is the right way to solve my problem. I need to parse an XML file and read items from it. Items can be things like orderLines, notes, attachments. The basic steps to get these items are all the same. How can I make 1 method which creates a list of these items and call a specific method to read an item?
public override IList<T> GetItems<T>(XPathNavigator currentOrder) where T : ISortableByLineNumber, new ()
{
var itemList = new List<T>();
var itemXmlNodes = currentOrder.Select(OrderXPath);
if (itemXmlNodes == null)
throw new Exception("");
var lineNumber = 1;
foreach (XPathNavigator itemXmlNode in itemXmlNodes)
{
var item = new T();
item = ReadItem(itemXmlNode, lineNumber++, item);
itemList.Add(item);
Logger.Debug(string.Format("Added item {0}", item));
}
return itemList;
}
I thought I could do this with the ReadItem method. I would create overloads for each type of item I would be reading.
private ISortableByLineNumber ReadItem(XPathNavigator itemXmlNode, int i, OrderLine item)
{
// specific code to read a orderLine
}
private ISortableByLineNumber ReadItem(XPathNavigator itemXmlNode, int i, Note item)
{
// specific code to read a note
}
But when I try to compile this could I get "The best overloaded method match for 'XmlOrderParser.XmlOrders.Prs3XmlFileWithOrders.ReadItem(System.Xml.XPath.XPathNavigator, int, XmlOrderParser.Entities.OrderLine)' has some invalid arguments". The problem is the compiler doesn't know how to cast T to OrderLine or Note.
If you are using .NET 4 you can make use of the new dynamic type by changing only one thing:
dynamic item = new T(); // instead of var item = new T();
Because item now is dynamic the runtime does an automatic overload resolution based on the actual type of item.
Please be aware that you will receive a runtime exception if T is a type for which no overload exists.
The following snippet demonstrates your problem (paste into LINQPad and choose "C# program" as language):
void Main()
{
Method<Class1>(); // Outputs Class1
Method<Class2>(); // Outputs Class2
Method<Class2b>(); // Outputs Class2, because it falls back to the base type
Method<Class3>(); // Throws exception
}
void Method<T>() where T : new()
{
dynamic c = new T();
Method(c);
}
void Method(Class1 c) { Console.WriteLine("Class1"); }
void Method(Class2 c) { Console.WriteLine("Class2"); }
class Class1 {}
class Class2 {}
class Class2b : Class2 {}
class Class3 {}
That's because at compile time you don't know the method you are going to need. To solve this, you need to find the right method and invoke it. Try something like:
MethodInfo mi = typeof(YourClass).GetMethod("ReadItem",
BindingFlags.NonPublic,
null,
new Type[] { typeof(XPathNavigator), typeof(int), typeof(T) },
null);
item = mi.Invoke(this, new object { itemXmlNode, lineNumber++, item });
Hope it helps. Good luck!
As you told in comment, your all classes OrderLine, Note etc. implement the ISortableByLineNumber interface, so you can change the definition of your method ReadItem to:
private OrderLine ReadItem(XPathNavigator itemXmlNode, int i, ISortableByLineNumber item)
{
}
and the actual type which is passed to the item param in above method would have its specific implementation executed at runtime, so if item is of type OrderLine when passed from GetItems<T> method then its specific implementation would be invoked and so on..
The above change in method definition would make your method definition interface specific and not concrete type specific which is one of the practices of interface based programming explained in below links:
http://visualstudiomagazine.com/articles/2010/01/01/interface-based-programming.aspx
What exactly is "interface based programming"?
This is a bit difficult to explain. So here it goes.
I have a function like this:
public T FooBar<T>(Func<T> function)
{
T returnData = function();
// want to iterate through returnData to do something to it
return returnData;
}
If the returnData (T) is an IEnumerable list, then I would like to enumerate through returnData to modify its contents using reflection. But I can't seem to be able to do it. When I try to cast returnData to an enumerable type, I get an exception:
Unable to cast object of type
'System.Collections.Generic.List`1[Cars]'
to type
'System.Collections.Generic.List`1[System.Object]'.
I will not know that the return type will be a list of 'cars' for example ahead of time, only at run time. So I have to check using reflection if it is a list, and then try to cast it so that I can enumerate through it.
Unless I am going about it the wrong way. How can I enumerate through returnData if it is of type T?
One approach is to add a type constraint on T, but this is not ideal:
public T FooBar<T>(Func<T> function) where T : IEnumerable
{
// T is not strongly typed for the enumerated item
If you changed your method slightly (w.r.t. T):
public IEnumerable<T> FooBar<T>(Func<IEnumerable<T>> function)
Then you have strong typing on the actual item being enumerated with the added bonus of accepting enumerable objects.
So I noticed from a second read of your question, there is some confusion about what T means for your variable returnData. In the case where FooBar() is passed a List<Car>, T is List<Car>, and really has no association with the generic type specification of the List<> itself. You can think of it as some List<U> where U is some other, unknown type.
At runtime you will have no simple way to get to U as it is hidden, so to speak, inside T. You could use overloading as some of the other answerers recommend, and provide a non-IEnumerable<U> method and one which takes arguments of type Func<IEnumerable<T>>.
Perhaps with some more details about the goal of FooBar<T> we could make some more specific recommendations.
if (returnData is System.Collections.IEnumerable)
{
foreach (object o in (System.Collections.IEnumerable)returnData)
{
// Do something.
}
}
Really, though, why not have an additional overload like this:
public T FooBar<T>(Func<IEnumerable<T>> function)
Have you tried type casting to IEnumerable instead of IEnumerable<T>? With IEnumerable you can still use it in a foreach loop. The variable each item would go in should be of type object i.e.:
foreach(object item in (IEnumerable)T){...}
You should check first to be sure that T implements IEnumerable.
The issue here is IEnumerable and IEnumerable Of T are not the same... but you can check for the difference and account for it in your code. Note that IEnumerable Of T inherits IEnumerable, so you can wrap the check for the generic version inside the non-generic version.
The following worked for me in a small test I wrote - I hope it is sufficient for you to do what you need.
Here is the meat and potatoes:
class FooBarOfT
{
public T FooBar<T>(Func<T> function)
{
T returnData = function();
//Want to iterate through returnData to do something to it.
if (returnData is IEnumerable)
{
// get generic type argument
var returnDataType = returnData.GetType();
if (returnDataType.IsGenericType)
{
// this is a System.Collections.Generic.IEnumerable<T> -- get the generic type argument to loop through it
Type genericArgument = returnDataType.GetGenericArguments()[0];
var genericEnumerator =
typeof(System.Collections.Generic.IEnumerable<>)
.MakeGenericType(genericArgument)
.GetMethod("GetEnumerator")
.Invoke(returnData, null);
IEnumerator enm = genericEnumerator as IEnumerator;
while (enm.MoveNext())
{
var item = enm.Current;
Console.WriteLine(string.Format("Type : {0}", item.GetType().Name));
}
}
else
{
// this is an System.Collections.IEnumerable (not generic)
foreach (var obj in (returnData as IEnumerable))
{
// do something with your object
}
}
}
return returnData;
}
}
I also set up some supporting test classes:
class Foo
{
private string _fooText;
public Foo(string fooText)
{
_fooText = fooText;
}
public string Execute()
{
return string.Format("executed! with {0} !", _fooText);
}
}
class Bar
{
public string BarContent { get; set; }
}
And a small console app to run some tests:
class Program
{
static void Main(string[] args)
{
// tests
Func<string> stringFunc = () =>
"hello!";
Func<List<Foo>> listFooFunc = () =>
new List<Foo>
{
new Foo("Hello!"),
new Foo("World!")
};
Func<IEnumerable> ienumerableFooFunc = () =>
new Hashtable
{
{ "ItemOne", "Foo" },
{ "ItemTwo", "Bar" }
};
var fooBarOfT = new FooBarOfT();
fooBarOfT.FooBar(stringFunc);
fooBarOfT.FooBar(listFooFunc);
fooBarOfT.FooBar(ienumerableFooFunc);
Console.ReadKey();
}
}
I have a class with a Items property, which is an IList:
class Stuff {
IList<OtherStuff> Items;
}
I want to be able to receive a string within a method (I thought of this format originally: Items[0]) and be able to retrieve the first item of the Items list.
I tried this:
object MyMethod(string s, object obj) {
return obj.GetType().GetProperty(s).GetValue(obj,null);
}
with s being 'Items[0]' but it doesn't work... Also tried parsing the parameter to access only the property 'Items' of the object and then accessing the index (knowing that it is an IList).
None of these approaches worked... Any thoughts?
Any thoughts?
You can access the property and then can you convert it to a list.
T GetListItem<T>(object obj, string property, int index)
{
return (obj.GetType().GetProperty(property).GetValue(obj, null) as IList<T>)[index];
}
Working example for your sample code:
OtherStuff item = GetListItem<OtherStuff>(obj, "Items", 0);
If you want to test an object to see if it has a numeric indexer, without regard to whether it is an IList, and then invoke the indexer via reflection, you can try out this method.
It returns true if the object has an indexer, and populates value with the value of the 0th index as well.
public static bool TryGetFirstIndexWithReflection(object o, out object value)
{
value = null;
// find an indexer taking only an integer...
var property = o.GetType().GetProperty("Item", new Type[] { typeof(int) });
// if the property exists, retrieve the value...
if (property != null)
{
value = property.GetValue(list, new object[] { 0 });
return true;
}
return false;
}
Note that this example makes no attempt to gracefully handle exceptions, such as IndexOutOfRangeException. That's up to you to add if you find it relevant.
Items was not a property, so my approaches wouldn't work. It should be, so I transformed it into a property and now it is working smoothly.
You should try this:
object GetFirstItemByReflection(object obj) {
return obj.GetType().GetMethod("get_Item").Invoke(obj, new object[] { 0 } );
}
with the appropriate checks.
"get_Item" is the "generated" method used when you access items by index in a collection.
When you get its MethodInfo, you invoke it on your collection, passing it the "0" parameter, to get the first item.