I have written a class, for copying properties from one object to another, but I caught exception:
System.Reflection.TargetException: The object does not match the target type.
I checked, that fromPropValue is of correct type, is not null, etc.
Of course, property of recepient is Binary.
public class Reflector
{
public void ReflectProperties(object from, object to)
{
Type toType = to.GetType();
Type fromType = from.GetType();
var toProperties = toType.GetProperties();
foreach (var prop in toProperties)
{
var fromProp = fromType.GetProperty(prop.Name);
if (fromProp != null)
{
var propType = prop.PropertyType;
var fromPropValue = fromProp.GetValue(from, null);
if (propType == typeof(Binary))
prop.SetValue(this, (Binary)fromPropValue, null); // <-- error
else if (propType == typeof(string))
prop.SetValue(this, (string)fromPropValue, null);
else if (propType == typeof(bool))
prop.SetValue(this, (bool)fromPropValue, null);
}
}
}
}
P.S.: object from is parent of object to, and i want just to copy values of all properties from parent to child.
I think you want prop.SetValue(to, ... instead of prop.SetValue(this, ....
Also you don't need the if statements and the casts. You can just do prop.SetValue(to, fromPropValue, null);
Related
I am trying to invoke a method via reflection with parameters and I get:
object does not match target type
If I invoke a method without parameters, it works fine. Based on the following code if I call the method Test("TestNoParameters"), it works fine. However if I call Test("Run"), I get an exception. Is something wrong with my code?
My initial purpose was to pass an array of objects e.g. public void Run(object[] options) but this did not work and I tried something simpler e.g. string without success.
// Assembly1.dll
namespace TestAssembly
{
public class Main
{
public void Run(string parameters)
{
// Do something...
}
public void TestNoParameters()
{
// Do something...
}
}
}
// Executing Assembly.exe
public class TestReflection
{
public void Test(string methodName)
{
Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
Type type = assembly.GetType("TestAssembly.Main");
if (type != null)
{
MethodInfo methodInfo = type.GetMethod(methodName);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(methodInfo, parametersArray);
}
}
}
}
}
Change "methodInfo" to "classInstance", just like in the call with the null parameter array.
result = methodInfo.Invoke(classInstance, parametersArray);
You have a bug right there
result = methodInfo.Invoke(methodInfo, parametersArray);
it should be
result = methodInfo.Invoke(classInstance, parametersArray);
A fundamental mistake is here:
result = methodInfo.Invoke(methodInfo, parametersArray);
You are invoking the method on an instance of MethodInfo. You need to pass in an instance of the type of object that you want to invoke on.
result = methodInfo.Invoke(classInstance, parametersArray);
The provided solution does not work for instances of types loaded from a remote assembly. To do that, here is a solution that works in all situations, which involves an explicit type re-mapping of the type returned through the CreateInstance call.
This is how I need to create my classInstance, as it was located in a remote assembly.
// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName);
However, even with the answer provided above, you'd still get the same error. Here is how to go about:
// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap();
// re-map the type to that of the object we retrieved
type = classInstace.GetType();
Then do as the other users mentioned here.
I tried to work with all the suggested answers above but nothing seems to work for me. So i am trying to explain what worked for me here.
I believe if you are calling some method like the Main below or even with a single parameter as in your question, you just have to change the type of parameter from string to object for this to work. I have a class like below
//Assembly.dll
namespace TestAssembly{
public class Main{
public void Hello()
{
var name = Console.ReadLine();
Console.WriteLine("Hello() called");
Console.WriteLine("Hello" + name + " at " + DateTime.Now);
}
public void Run(string parameters)
{
Console.WriteLine("Run() called");
Console.Write("You typed:" + parameters);
}
public static string StaticString()
{
return "static string example";
}
public string TestNoParameters()
{
Console.WriteLine("TestNoParameters() called");
return ("TestNoParameters() called");
}
public void Execute(object[] parameters)
{
Console.WriteLine("Execute() called");
Console.WriteLine("Number of parameters received: " + parameters.Length);
for(int i=0;i<parameters.Length;i++){
Console.WriteLine(parameters[i]);
}
}
}
}
Then you have to pass the parameterArray inside an object array like below while invoking it. The following method is what you need to work
private object ExecuteWithReflection(string methodName,object parameterObject = null)
{
Assembly assembly = Assembly.LoadFile("Assembly.dll");
Type typeInstance = assembly.GetType("TestAssembly.Main");
MethodInfo methodInfo = typeInstance.GetMethod(methodName);
ParameterInfo[] parameterInfo = methodInfo.GetParameters();
object result = null;
if (typeInstance != null) //non static
{
if(methodInfo.IsStatic == false)
{
//instance is needed to invoke the method
object classInstance = Activator.CreateInstance(typeInstance, null);
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(classInstance, null);
}
else
{
result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
}
}
else //handle static
{
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(null, null);
}
else
{
result = methodInfo.Invoke(null,new object[] { parameterObject } );
}
}
}
return result;
}
This method makes it easy to invoke the method, it can be called as following
ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
ExecuteWithReflection("StaticString");
I'am posting this answer because many visitors enter here from google for this problem.
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );
when external .dll -instead of this.GetType(), you might use typeof(YourClass).
I would use it like this, its way shorter and it won't give any problems
dynamic result = null;
if (methodInfo != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
}
Assembly assembly = Assembly.LoadFile(#"....bin\Debug\TestCases.dll");
//get all types
var testTypes = from t in assembly.GetTypes()
let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
where attributes != null && attributes.Length > 0
orderby t.Name
select t;
foreach (var type in testTypes)
{
//get test method in types.
var testMethods = from m in type.GetMethods()
let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
where attributes != null && attributes.Length > 0
orderby m.Name
select m;
foreach (var method in testMethods)
{
MethodInfo methodInfo = type.GetMethod(method.Name);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(classInstance, parametersArray);
}
}
}
}
I m invoking the weighted average through reflection. And had used method with more than one parameter.
Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file
Object weightedobj = cls.newInstance(); // invoke empty constructor
Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method
On .Net 4.7.2 to invoke a method inside a class loaded from an external assembly you can use the following code in VB.net
Dim assembly As Reflection.Assembly = Nothing
Try
assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
Dim iniClass = Activator.CreateInstance(typeIni, True)
Dim methodInfo = typeIni.GetMethod("nameOfMethod")
'replace nothing by a parameter array if you need to pass var. paramenters
Dim parametersArray As Object() = New Object() {...}
'without parameters is like this
Dim result = methodInfo.Invoke(iniClass, Nothing)
Catch ex As Exception
MsgBox("Error initializing main layout:" & ex.Message)
Application.Exit()
Exit Sub
End Try
I'm trying to call the equivalent of this code via reflection:
prop.WhenAnyValue(s => s.Value).Skip(1).Subscribe(...);
This is the reflection code I use to "solve" the WhenAnyValue part:
MethodInfo[] whenAnyMethods = typeof(WhenAnyMixin).GetMethods();
MethodInfo whenAnyMethod = new Func<MethodInfo>(() =>
{
foreach (var mi in whenAnyMethods)
{
if (mi.Name == "WhenAnyValue" && mi.GetGenericArguments().Length == 2)
{
return mi;
}
}
return null;
})();
MethodInfo whenAnyMethodGeneric = whenAnyMethod.MakeGenericMethod(new Type[] { containingType, propertyType });
var whenAnyResult = whenAnyMethodGeneric.Invoke(null, new object[] { containingObject, expression });
The containing object derives from ReactiveObject (ReactiveUI).
The type of the result is exactly the same as when I'm calling WhenAnyValue directly on a property I'm interested in (without reflection), so I think I got this part right. :)
Now I'm trying to skip:
MethodInfo[] methods = typeof(Observable).GetMethods();
MethodInfo skipMethod = new Func<MethodInfo>(() =>
{
foreach (var mi in methods)
{
if (mi.Name == "Skip" && mi.GetParameters().Length == 2 && mi.GetParameters()[1].ParameterType == typeof(int))
{
return mi;
}
}
return null;
})();
MethodInfo skipMethodGeneric = skipMethod.MakeGenericMethod(new Type[] { whenAnyResult.GetType() });
var skipResult = skipMethodGeneric.Invoke(null, new object[] { whenAnyResult, 1 }); <<< this line throws the exception
On execution of the last line an exception is thrown:
System.ArgumentException: "Object of type 'System.Reactive.Linq.ObservableImpl.Select`2+Selector[ReactiveUI.IObservedChange`2[type1,type2],type3]' cannot be converted to type 'System.IObservable`1[System.Reactive.Linq.ObservableImpl.Select`2+Selector[ReactiveUI.IObservedChange`2[type1,type2],type3]]'."
And here I'm stuck. What am I missing?
This question already has answers here:
Passing dynamic object to C# method changes return type
(2 answers)
Closed 3 years ago.
I have a problem passing dynamic parameter to the async method. Compile time error says Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable<dynamic>' to 'System.Collections.Generic.IEnumerable<System.Threading.Tasks.Task>'.
Actually, I am trying to log entries for each DB table column entries. So, I am using a generic method which takes dynamic oldervalue and newvalue and perform insertion into the AuditDB.
Code
public async Task Translog(int Id, string Action, DateTime RecordTimeStamp, dynamic oldervalue, dynamic newvalue, VIBRANT db, VibrantAuditEntities context)
{
try
{
#region FrameLog
Type typeOfMyObject;
if (oldervalue != null)
typeOfMyObject = oldervalue.GetType();
else
typeOfMyObject = newvalue.GetType();
PropertyInfo[] properties = typeOfMyObject.GetProperties();
var tasks = properties.ToList().Select(i => SetPropValuess(typeOfMyObject, i, Id, Action, RecordTimeStamp, oldervalue, newvalue, db, context));
await Task.WhenAll(tasks); // Compile error at this line
#endregion
}
catch
{
throw;
}
}
public async Task SetPropValuess(Type typeOfMyObject, PropertyInfo item, int Id, string Action, DateTime RecordTimeStamp, dynamic oldervalue, dynamic newvalue, VIBRANT db, VibrantAuditEntities context)
{
await Task.Run(() =>
{
string PropertyTypeNamespace = typeOfMyObject.GetProperty(item.Name).PropertyType.Namespace;
if (PropertyTypeNamespace != "System.Collections.Generic" && PropertyTypeNamespace != "ClassLibrary")
{
if (oldervalue != null && newvalue != null)
{
//Edit
bool chk = Comparer.Equals(oldervalue.GetType().GetProperty(item.Name).GetValue(oldervalue, null), newvalue.GetType().GetProperty(item.Name).GetValue(newvalue, null));
if (chk == false)
{
// make and add log record
AuditLog al = new AuditLog();
al.TableName = (typeOfMyObject.BaseType.Name == "Object" ? typeOfMyObject.Name : typeOfMyObject.BaseType.Name);
al.EventDateUTC = RecordTimeStamp;
al.OriginalValue = Convert.ToString(oldervalue.GetType().GetProperty(item.Name).GetValue(oldervalue, null));
al.NewValue = Convert.ToString(newvalue.GetType().GetProperty(item.Name).GetValue(newvalue, null));
al.ColumnName = item.Name;
context.AuditLogs.Add(al);
}
}
else if (oldervalue != null && newvalue == null)
{
//Delete
AuditLog al = new AuditLog();
al.TableName = (typeOfMyObject.BaseType.Name == "Object" ? typeOfMyObject.Name : typeOfMyObject.BaseType.Name);
al.EventDateUTC = RecordTimeStamp;
al.OriginalValue = Convert.ToString(typeOfMyObject.GetProperty(item.Name).GetValue(oldervalue, null));
al.NewValue = null;
al.ColumnName = item.Name;
al.EventType = Action;
context.AuditLogs.Add(al);
}
else if (oldervalue == null && newvalue != null)
{
//Create
AuditLog al = new AuditLog();
al.TableName = (typeOfMyObject.BaseType.Name == "Object" ? typeOfMyObject.Name : typeOfMyObject.BaseType.Name);
al.EventDateUTC = RecordTimeStamp;
al.OriginalValue = null;
al.NewValue = Convert.ToString(typeOfMyObject.GetProperty(item.Name).GetValue(newvalue, null));
al.ColumnName = item.Name;
context.AuditLogs.Add(al);
}
}
});
}
It's very strange, for some reason the fact that there is a parameter of type 'dynamic' affects the type of Tasks. If the dynamic parameters are removed then it works. As a workaround you can add a cast, but would be interesting to understand why this happens:.
var tasks = properties
.ToList()
.Select(i => SetPropValuess(typeOfMyObject, i, Id, Action, RecordTimeStamp, oldervalue, newvalue, db, context))
.Cast<Task>();
I have a fairly simple method:
public static LinkItemCollection ToList<T>(this LinkItemCollection linkItemCollection)
{
var liCollection = linkItemCollection.ToList(true);
var newCollection = new LinkItemCollection();
foreach (var linkItem in liCollection)
{
var contentReference = linkItem.ToContentReference();
if (contentReference == null || contentReference == ContentReference.EmptyReference)
continue;
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
IContentData content = null;
var epiObject = contentLoader.TryGet(contentReference, out content);
if (content is T)
newCollection.Add(linkItem);
}
return newCollection;
}
This works fine - I can call the method and provide a type as T. However, what I want to be able to do is to be able to specify multiple types. I therefore, wrongly, assumed I could refactor the method to:
public static LinkItemCollection ToList(this LinkItemCollection linkItemCollection, Type[] types)
{
var liCollection = linkItemCollection.ToList(true);
var newCollection = new LinkItemCollection();
foreach (var linkItem in liCollection)
{
var contentReference = linkItem.ToContentReference();
if (contentReference == null || contentReference == ContentReference.EmptyReference)
continue;
foreach (var type in types)
{
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
IContentData content = null;
var epiObject = contentLoader.TryGet(contentReference, out content);
if (content is type)
newCollection.Add(linkItem);
}
}
return newCollection;
}
However, Visual Studio is showing it cannot resolve the symbol for type on the line if(content is type).
I know I'm doing something wrong, and I'm guessing I need to use Reflection here.
What you're looking for is:
type.IsAssignableFrom(content.GetType())
is is only used for checking against a type known at compile time, not at runtime.
Question:
I wrote a method to retrieve a SQL result as a list of a class instead of a datatable.
The problem is, I have a int field in the database, which is nullable.
If I hit a row with a NULL int, DataReader returns DbNull.Value instead of null.
So System.Convert.ChangeType(objVal, fi.FieldType) throws an exception, because it can't convert DbNull to an int.
So far so bad.
I thought I had solved the problem, when I just compared objVal to DbNull.Value and if true, did this instead:
System.Convert.ChangeType(null, fi.FieldType)
unfortunately, I just realized, the resulting integer type is 0 instead of NULL.
So I just tried changing the int type in my class to Nullable<int>, but now I have the problem that when a value is not DbNull.Value, ChangeType throws an exception because it can't convert int to nullable<int>...
So now I try to detect the type of the object returned by datareader, and convert it to a nullable value.
tTypeForNullable is correctly shown as Nullable<int>.
But when I look at the result type, I get: int.
Why is that ? And more important: How can I do that properly ?
Please note that because type is an object, I can't use a generic method to create Nullable<int>.
bool bisnull = IsNullable(objVal);
bool bisnullt = IsNullable(fi.FieldType);
if (bisnullt)
{
Type tTypeForNullable = typeof(Nullable<>).MakeGenericType(objVal.GetType());
//object result = Activator.CreateInstance(tTypeForNullable, new object[] { objVal });
//object result = Activator.CreateInstance(typeof(Nullable<int>), new object[] { objVal });
object result = Activator.CreateInstance(tTypeForNullable, objVal);
Type tres = result.GetType();
fi.SetValue(tThisValue, System.Convert.ChangeType(result, fi.FieldType));
}
Here's the complete routine for reference:
public virtual System.Collections.Generic.IList<T> GetList<T>(System.Data.IDbCommand cmd)
{
System.Collections.Generic.List<T> lsReturnValue = new System.Collections.Generic.List<T>();
T tThisValue = default(T);
Type t = typeof(T);
lock (cmd)
{
using (System.Data.IDataReader idr = ExecuteReader(cmd))
{
lock (idr)
{
while (idr.Read())
{
//idr.GetOrdinal("")
tThisValue = Activator.CreateInstance<T>();
// Console.WriteLine(idr.FieldCount);
for (int i = 0; i < idr.FieldCount; ++i)
{
string strName = idr.GetName(i);
object objVal = idr.GetValue(i);
System.Reflection.FieldInfo fi = t.GetField(strName);
//Type tttttt = fi.FieldType;
if (fi != null)
{
//fi.SetValue(tThisValue, System.Convert.ChangeType(objVal, fi.FieldType));
if (objVal == System.DBNull.Value)
{
objVal = null;
fi.SetValue(tThisValue, null);
}
else
{
//System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(fi.FieldType);
bool bisnull = IsNullable(objVal);
bool bisnullt = IsNullable(fi.FieldType);
if (bisnullt)
{
Type tTypeForNullable = typeof(Nullable<>).MakeGenericType(objVal.GetType());
//object result = Activator.CreateInstance(tTypeForNullable, new object[] { objVal });
//object result = Activator.CreateInstance(typeof(Nullable<int>), new object[] { objVal });
object result = Activator.CreateInstance(tTypeForNullable, objVal);
Type tres = result.GetType();
fi.SetValue(tThisValue, System.Convert.ChangeType(result, fi.FieldType));
}
fi.SetValue(tThisValue, System.Convert.ChangeType(objVal, fi.FieldType));
}
}
else
{
System.Reflection.PropertyInfo pi = t.GetProperty(strName);
if (pi != null)
{
//pi.SetValue(tThisValue, System.Convert.ChangeType(objVal, pi.PropertyType), null);
if (objVal == System.DBNull.Value)
{
objVal = null;
pi.SetValue(tThisValue, null, null);
}
else
{
pi.SetValue(tThisValue, System.Convert.ChangeType(objVal, pi.PropertyType), null);
}
}
// Else silently ignore value
} // End else of if (fi != null)
//Console.WriteLine(strName);
} // Next i
lsReturnValue.Add(tThisValue);
} // Whend
idr.Close();
} // End Lock idr
} // End Using idr
} // End lock cmd
return lsReturnValue;
} // End Function GetList
with this:
public System.Data.IDataReader ExecuteReader(System.Data.IDbCommand cmd)
{
System.Data.IDataReader idr = null;
lock(cmd)
{
System.Data.IDbConnection idbc = GetConnection();
cmd.Connection = idbc;
if (cmd.Connection.State != System.Data.ConnectionState.Open)
cmd.Connection.Open();
idr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
} // End Lock cmd
return idr;
} // End Function ExecuteReader
Please note that because type is an object, I can't use a generic method to create Nullable<int>.
You're boxing - and the result of a boxing operation for a nullable value type is never a boxed value of that type. It's either null or a non-nullable value type. See MSDN for more information.