i create an instance of a Person class using reflection and execute its constructor and after that i execute another function of the person class called "Execute":
Assembly assembly = Assembly.GetEntryAssembly();
object personObject = assembly.CreateInstance("ReflectionTest.Person");
// Call Constructor
var ctor = personObject.GetType().GetConstructor(new Type[] { typeof(int) });
var obj = ctor.Invoke(new object[] { 10 });
// Call Method
MethodInfo methodInfo = personObject.GetType().GetMethod("Execute");
object obj1 = methodInfo.Invoke(personObject, null);
Th problem is that all the person class variables i instanciated in the constructor ARE NULL when i call the "Execute" method. why ? and how do i get around this?
In your example, you are invoking the default constructor with this line:
object personObject = assembly.CreateInstance("ReflectionTest.Person");
This would be the proper way to construct the object:
Assembly assembly = Assembly.GetEntryAssembly();
Type personType = assembly.GetType("ReflectionTest.Person");
object inst = Activator.CreateInstance(personType, new object[] { 10 });
I'm not 100%, but could casting obj to a Person help?
Related
I am using Moq 4.18.1. I have to setup a mock for the following method signature:
Task AddMultiMessagesWithTransactionAsync<T>(List<(string queueName, T content, string messageId)> messageInfos, bool createQueue = false) where T : class;
The special thing is the generic type inside the tupel, which is at the first parameter. And I think there lies the problem. Other mock-setups in the same project are working fine. The mock looks like:
Mock<IServiceBusService> sbSrvMock2 = new();
var messageInfos = It.IsAny<List<(string queueName, It.IsAnyType content, string messageId)>>();
var createQueue = It.IsAny<bool>();
sbSrvMock2
.Setup(sb => sb.AddMultiMessagesWithTransactionAsync<It.IsAnyType>(messageInfos, createQueue))
.Callback(new InvocationAction((inv) =>
{
var p1 = inv.Arguments[0];
var p2 = inv.Arguments[1];
Console.WriteLine("I was here");
}));
The callback is never triggered! Do I have a problem with setting up the signature? Is it something with async?
Simple.OData.Client has a typed and dynamic (and basic) syntax.
I like the typed, but I don't want to build out all my types. In the end I really only need two or so types in the results I get.
But my queries need more types to properly filter the results.
So I want to use the dynamic syntax. But I want to cast the results to classes I have.
I can easily do this manually, but I thought I would see if Simple.OData.Client supports this before I go writing up all that conversion code for each query.
Here is some dynamic syntax code that runs without errors:
client.For(x.Client).Top(10).Select(x.ClientId, x.Name).FindEntriesAsync();
Here is an example of what I had hoped would work (selecting into a new Client object)
client.For(x.Client).Top(10).Select(new Client(x.ClientId, x.Name)).FindEntriesAsync();
But that kind of projection is not supported (I get an "has some invalid arguments" error).
Is there a way to support projection into an existing class when using the dynamic syntax of Simple.OData.Client?
EDIT: The code below works. But it's performance is terrible. I decided to abandon it and write hand written mappers for each type I needed.
This is what I came up with:
dynamic results = oDataClient.For(x.Client).Select(x.ClientId, x.Name).FindEntriesAsync().Result;
var listOfClients = SimpleODataToList<Client>(results);
public List<T> SimpleODataToList<T>(dynamic sourceObjects) where T : new()
{
List<T> targetList = new List<T>();
foreach (var sourceObject in sourceObjects)
{
// This is a dictionary with keys (properties) and values. But no
// matter what sourceObject is passed in, the values will always be
// the values of the first entry in the sourceObjects list.
var sourceProperties = ((System.Collections.Generic.IDictionary<string, object>)sourceObject);
var targetProperties = typeof(Client).GetProperties().Where(prop => prop.CanWrite);
var targetObject = new T();
foreach (var targetProperty in targetProperties)
{
if (sourceProperties.ContainsKey(targetProperty.Name))
{
var sourceValue = GetProperty(sourceObject, targetProperty.Name);
targetProperty.SetValue(targetObject, sourceValue, null);
}
}
targetList.Add(targetObject);
}
return targetList;
}
public static object GetProperty(object o, string member)
{
if (o == null) throw new ArgumentNullException("o");
if (member == null) throw new ArgumentNullException("member");
Type scope = o.GetType();
IDynamicMetaObjectProvider provider = o as IDynamicMetaObjectProvider;
if (provider != null)
{
ParameterExpression param = Expression.Parameter(typeof(object));
DynamicMetaObject mobj = provider.GetMetaObject(param);
GetMemberBinder binder = (GetMemberBinder)Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, member, scope, new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null) });
DynamicMetaObject ret = mobj.BindGetMember(binder);
BlockExpression final = Expression.Block(
Expression.Label(CallSiteBinder.UpdateLabel),
ret.Expression
);
LambdaExpression lambda = Expression.Lambda(final, param);
Delegate del = lambda.Compile();
return del.DynamicInvoke(o);
}
else
{
return o.GetType().GetProperty(member, BindingFlags.Public | BindingFlags.Instance).GetValue(o, null);
}
}
It was made much harder because normal casts and such for the dynamic objects returned would only give the first object in the list over and over. The GetProperty method works around this limitation.
// I use this simple program:
public static Object convertToBean(Class type, Map map) {
BeanInfo beanInfo;
Object obj = null;
try {
beanInfo = Introspector.getBeanInfo(type);
obj = type.newInstance();
// When I debugging to here, I found that some properties is different from the variable the Object own. PropertyDescriptor changes charactor case when the variable is not in "String" type.
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : propertyDescriptors) {
String propertyName = descriptor.getName();
if (map.containsKey(propertyName)) {
Object value = map.get(propertyName);
Object[] args = new Object[1];
args[0] = value;
descriptor.getWriteMethod().invoke(obj, args);
}
}
} catch (Exception ignored) {
}
return obj;
}
//Using BeanMap is the same question.
Finally I found the root cause.
The problem solved by changing “A01” to "a01".
The variable name must be strict camel rule. First character must be lower case, except first two characters are all in upper case, like "AD".
Because the setter and getter methods will generate in same pattern. so It'll be difficult to recognize the real name of one variable.
How come when I create a Map it requires new and it is not required on an Object?
var o = new Object();
var m = new Map();
var constructorObject = o.constructor;
var constructorMap = m.constructor;
var newObject = constructorObject();
var newMap = constructorMap(); //splat
That last line produces,
TypeError: Constructor Map requires 'new'
at Map (native)
This simply depends on the implementation of the Map feature. You get this error in Chrome, while in Nightly calling Map(); returns you a beautiful brand new Map.
Keep in mind that the fact that you can create objects without using the new operator is just more or less useless (one could argue that it serves a purpose for the Object constructor as it doesn't simply return a new object, but an object which type matches the type of argument you passed). You could do the same for any constructor:
function Test() {
if(!(this instanceof Test)) return new Test;
}
Test(); // equivalent to *new Test;*
I think I've established that in as3corelib JSON.decode I have no choice but to deserialise to a plain old flex object.
var data:Object = JSON.decode(json);
If I then want to get the data contained in the object into another type I can't use type casting. I have to instantiate a new instance and add the properties manually.
var data:Object = JSON.decode(json);
var model:Model = new Model();
model.name = data.name;
model.notes = data.notes;
A pain and a bit ugly, but I'm guessing this is the price to be paid for going from untyped json to a flex type. My first question is whether my assumption is correct and there is no prettier way to create my model instance with the data contained within the json?
My second question, if so then before I write my own method to do this, is there anything inside the flex api that will take the data object and mixin it's values to my model instance?
Cheers,
Chris
the approach I've always used proved to be part of the AMF3 serialization mechanism in ActionScript.
have a look at IExternalizable and registerClassAlias.
now what I use is the following:
interface ISerializable {
public function getRawData():Object;
public function setRawData(param:Object):void;
}
function registerType(id:String, type:Class):void {
//implementation
}
function getTypeByID(id:String):Class {
//implementation
}
function getTypeID(type:Class):String {
//implementation
}
and to the decoder/encoder you register a class alias.
serialization of an object works as follows:
var raw:Object = model.getRawData();
raw[" type"] = getTypeID(model);
var encoded:String = JSON.encode(raw);
decoding works as follows:
var raw:Object = JSON.decode(raw);
var cl:Class = getTypeByID(raw[" type"]);
if (cl == null) throw new Error("no class registered for type: "+raw[" type"]);
delete raw[" type"];
var model:ISerializable = new cl();
model.setRawData(raw);
you will need to do this recursively on the whole deserialized JSON tree, starting at the leafs.
For cyclic reference, you'll need a trick.
I had an implementation of this somewhere, but I can't find it.
You can loop within the field of you json decoded object and assign them into your model:
function json2model(json:String):Model{
var data:Object = JSON.decode(json);
var m:Model=new Model();
for (var field:String in data) {
if (m.hasOwnProperty(field)) {
m[field] = data[field];
}
}
return m;
}
var model:Model=json2model(json)
or add a static function within your Model if you preffer:
public class Model {
//...
public static function fromJSon(json:String):Model {
var data:Object = JSON.decode(json);
var m:Model=new Model();
for (var field:String in data) {
if (m.hasOwnProperty(field)) {
m[field] = data[field];
}
}
return m;
}
}
}
var model:Model=Model.fromJSon(json);