I want to store list of objects in X++. I've read in msdn that arrays and containers cannot store Objects, so the only option is to make a list of Collection. I have written the following code and tried to use Collection = new List(Types::AnyType); and Collection = new List(Types::Classes); but both are not working. Please see if I'm making some mistake in the following job.
static void TestList(Args _args)
{
List Collection;
ListIterator iter;
anytype iVar, sVar, oVar;
PlmSizeRange PlmSizeRange;
;
Collection = new List(Types::AnyType);
iVar = 1;
sVar = "abc";
oVar = PlmSizeRange;
Collection.addEnd(iVar);
Collection.addEnd(sVar);
Collection.addEnd(oVar);
iter = new ListIterator(Collection);
while (iter.more())
{
info(any2str(iter.value()));
iter.next();
}
}
Moreover, can't we cast some variable or object into Anytype variable, I read out that typecasting is done automatically this way;
anytype iVar;
iVar = 1;
But on running it throws an error that expected type was Anytype, but encountered type is int.
Last things first, anytype variables takes the type first assigned to it, you cannot change it later:
static void Job2(Args _args)
{
anytype iVar;
iVar = 1; //Works, iVar is now an int!
iVar = "abc"; //Does not work, as iVar is now bound to int, assigns 0
info(iVar);
}
Back to your first question, new List(Types::AnyType) will never work as the addEnd method tests the type of its argument at runtime, and anytype variables will have the type of the value assigned to it.
Also new List(Types::Object) will only store objects, not simple data types as int and str.
It may be contrary to your what you (and C#) believe, but simple types are not objects.
What is left? Containers:
static void TestList(Args _args)
{
List collection = new List(Types::Container);
ListIterator iter;
int iVar;
str sVar;
Object oVar;
container c;
;
iVar = 1;
sVar = "abc";
oVar = new Object();
collection.addEnd([iVar]);
collection.addEnd([sVar]);
collection.addEnd([oVar.toString()]);
iter = new ListIterator(collection);
while (iter.more())
{
c = iter.value();
info(conPeek(c,1));
iter.next();
}
}
Objects do not automatically convert to containers, usually you supply pack and unpack methods (implementing the interface SysPackable). In the above code toString is used which is cheating.
On the other hand, I do not see a use case for your request, that Lists should contain any type. It is against its designed purpose, a List contain one and only one type as defined when the List object is created.
Besides lists there are other collections types, maybe a Struct will suit your needs.
Related
I basically have this:
Obj1 Create event:
health_total = 50;
health_current = health_total;
health_text = instance_create(x,y-10,obj_health); // Object to show health of an instance object
health_text.origin = self; // Assign an 'origin' variable so I can access it later?
obj_health Draw event:
show_debug_message(origin.x); // <-- This works just great!
show_debug_message(origin.health_current); // <-- This throws error :(
I assume that the variable might be local but then, how do I make it public? GML is a bit new to me, though, I'm not new to programming. This makes my mind hurt.
Use id, not self:
health_text.origin = id;
I have to dynamically execute queries which will come from database.The query has dynamic fields,which needs to be converted into map as key value pairs and send to view.For ex
one query may return only one fields and other may return more than two field of multiple rows.I have to write code in such way that it will work for n no.of fields and return it as map using spring jdbc.
Spring offers two ways to solve your problem.
Approach 1: use queryForList method from JdbcTemplate class. this will return List of Map populated by column names as key , and DB record as value. you have to manualy iterate over the list. each map object inside the list represents a single row in resultset.
example :
List<Map<String, Object>> result = jdbcTemplate.queryForList(query, new Object[]{123});
Iterator items = result.iterator();
while(items.hasNext()){
Map<String, Object> row = (Map<String, Object>) items.next();
System.out.println(row);
}
Approach 2 : this dosen't exactly match your requirements, but little faster than the first approach also more coding involved. you can use queryForRowSet method.
SqlRowSet rowSet = jdbcTemplate.queryForRowSet(query, new Object[]{3576});
int columnCount = rowSet.getMetaData().getColumnCount();
System.out.println(columnCount);
while(rowSet.next()){
for(int id =1 ; id <= columnCount ; id ++){
System.out.println(rowSet.getString(id)) ;
// your custom logic goes here
}
}
I am trying to build a dynamic Property Accessor. Want something which is like really fast as close to calling the actually Property. Dont want to go the Reflection route as its very slow. So i opted to using DynamicAssembly and inject IL using ILGenerator. Below is the ILGenerator related code which seems to work
Label nulllabel = getIL.DefineLabel();
Label returnlabel = getIL.DefineLabel();
//_type = targetGetMethod.ReturnType;
if (methods.Count > 0)
{
getIL.DeclareLocal(typeof(object));
getIL.DeclareLocal(typeof(bool));
getIL.Emit(OpCodes.Ldarg_1); //Load the first argument
//(target object)
//Cast to the source type
getIL.Emit(OpCodes.Castclass, this.mTargetType);
//Get the property value
foreach (var methodInfo in methods)
{
getIL.EmitCall(OpCodes.Call, methodInfo, null);
if (methodInfo.ReturnType.IsValueType)
{
getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
//Box if necessary
}
}
getIL.Emit(OpCodes.Stloc_0); //Store it
getIL.Emit(OpCodes.Br_S,returnlabel);
getIL.MarkLabel(nulllabel);
getIL.Emit(OpCodes.Ldnull);
getIL.Emit(OpCodes.Stloc_0);
getIL.MarkLabel(returnlabel);
getIL.Emit(OpCodes.Ldloc_0);
}
else
{
getIL.ThrowException(typeof(MissingMethodException));
}
getIL.Emit(OpCodes.Ret);
So above get the first argument which is the object that contains the property. the methods collection contains the nested property if any. for each property i use EmitCall which puts the the value on the stack and then i try to box it. This works like a charm.
The only issue is if you have a property like Order.Instrument.Symbol.Name and assume that Instrument object is null. Then the code will throw an null object exception.
So this what i did, i introduced a null check
foreach (var methodInfo in methods)
{
getIL.EmitCall(OpCodes.Call, methodInfo, null);
getIL.Emit(OpCodes.Stloc_0);
getIL.Emit(OpCodes.Ldloc_0);
getIL.Emit(OpCodes.Ldnull);
getIL.Emit(OpCodes.Ceq);
getIL.Emit(OpCodes.Stloc_1);
getIL.Emit(OpCodes.Ldloc_1);
getIL.Emit(OpCodes.Brtrue_S, nulllabel);
getIL.Emit(OpCodes.Ldloc_0);
if (methodInfo.ReturnType.IsValueType)
{
getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
//Box if necessary
}
}
Now this code breaks saying That the object/memory is corrupted etc. So what exactly is wrong with this code. Am i missing something here.
Thanks in Advance.
Previously, if you had consecutive properties P returning string and then Q returning int, you would get something like this:
...
call P // returns string
call Q // requires a string on the stack, returns an int
box
...
Now you have something like this:
...
call P // returns string
store // stores to object
... // load, compare to null, etc.
load // loads an *object*
call Q // requires a *string* on the stack
store // stores to object *without boxing*
...
So I see two clear problems:
You are calling methods in such a way that the target is only known to be an object, not a specific type which has that method.
You are not boxing value types before storing them to a local of type object.
These can be solved by reworking your logic slightly. There are also a few other minor details you could clean up:
Rather than ceq followed by brtrue, just use beq.
There's no point in doing Stloc_1 followed by Ldloc_1 rather than just using the value on the stack since that local isn't used anywhere else.
Incorporating these changes, here's what I'd do:
Type finalType = null;
foreach (var methodInfo in methods)
{
finalType = methodInfo.ReturnType;
getIL.EmitCall(OpCodes.Call, methodInfo, null);
if (!finalType.IsValueType)
{
getIL.Emit(OpCodes.Dup);
getIL.Emit(OpCodes.Ldnull);
getIL.Emit(OpCodes.Beq_S, nulllabel);
}
}
if (finalType.IsValueType)
{
getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
//Box if necessary
}
getIL.Emit(OpCodes.Br_S, returnLabel);
getIL.MarkLabel(nulllabel);
getIL.Emit(OpCodes.Pop);
getIL.Emit(OpCodes.Ldnull);
getIL.MarkLabel(returnlabel);
Note that we can get rid of both locals since we now just duplicate the top value on the stack before comparing against null.
I'm writing some code updating database with a SQL statement that has some placeholders . But it doesn't seem to update these placeholders.
I got the following error:
Cannot update '#columnName'; field not updateable
Here is the method:
public void updateDoctorTableField(string columnName, string newValue, string vendorNumber) {
sqlStatement = "update Doctor set #columnName = #newValue where `VENDOR #` = #vendorNumber;";
try {
_command = new OleDbCommand(sqlStatement, _connection);
_command.Parameters.Add("#columnName", OleDbType.WChar).Value = columnName;
_command.Parameters.Add("#newValue", OleDbType.WChar).Value = newValue;
_command.Parameters.Add("#vendorNumber", OleDbType.WChar).Value = vendorNumber;
_command.ExecuteNonQuery();
} catch (Exception ex) {
processExeption(ex);
} finally {
_connection.Close();
}
}
Not all parts of the query are parameterisable.
You can't parametrise the name of the column. This needs to be specified explicitly in your query text.
If this is sent via user input you need to take care against SQL Injection. In fact in any event it would be best to check it against a whitelist of known valid column names.
The reason the language does not allow for parameters for things like table names, column names and such is exactly the same reason why your C# program does not allow for substitution of variables in the code. Basically your question can be rephrased like this in a C# program:
class MyClass
{
int x;
float y;
string z;
void DoSomething(string variableName)
{
this.#variable = ...
}
}
MyCLass my = new MyClass();
my.DoSomething("x"); // expect this to manuipulate my.x
my.DoSomething("y"); // expect this to manuipulate my.y
my.DoSomething("z"); // expect this to manuipulate my.z
This obviously won't compile, because the compiler cannot generate the code. Same for T-SQL: the compiler cannot generate the code to locate the column "#columnName" in your case. And just as in C# you would use reflection to do this kind of tricks, in T-SQL you would use dynamic SQL to achieve the same.
You can (and should) use the QUOTENAME function when building your dynamic SQL to guard against SQL injection.
I have 2 dynamic objects and I want to build one to contain all the properties:
var o1:Object = {prop1:val1,prop2:val2,prop3:val3};
var o2:Object = {prop3:val3a,prop4:val4};
and I need to obtain a third object that looks like that:
{prop1:val1, prop2:val2, prop3:val3a, prop4:val4};
Basically I need a way to iterate through the object properties and to add new properties to the third object. I have to mention I'm quite new to AS3/Flash/Flex.
First question, do you really mean to have prop3 in both objects? you will need to decide what to do in case of a collision like that, which object has precedence.
Secondly, check out the introspection apis: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
something like this should work:
public function mergeDynamicObjects ( objectA:Object, objectB:Object ) : Object
{
var objectC:Object = new Object();
var p:String;
for (p in objectA) {
objectC[p] = objectA[p];
}
for (p in objectB) {
objectC[p] = objectB[p];
}
return objectC;
}
If the property exists in A and B, B's will overwrite A's. Also note that if the values of a property is an object, it will pass a reference, not a copy of the value. You might need to clone the object in those cases, depending on your needs.
Note: I haven't actually tested the above, but it should be close. Let me know if it doesn't work.
Updated to fix the errors. Glad it works for you though.
You can dynamically access/set properties on objects with the index operator. The for loop will itterate over the property names, so if you put it all together, the following test passes:
[Test]
public function merge_objects():void {
var o1:Object = {prop1:"one", prop2:"two", prop3:"three"};
var o2:Object = {prop3:"threeA", prop4:"four"};
var o3:Object = new Object();
for (var prop in o1) o3[prop] = o1[prop];
for (var prop in o2) o3[prop] = o2[prop];
assertThat(o3.prop1, equalTo("one"));
assertThat(o3.prop2, equalTo("two"));
assertThat(o3.prop3, equalTo("threeA"));
assertThat(o3.prop4, equalTo("four"));
}
you can iterate over the object properties like:
var obj1:Object = new Object();
for(var str:String in obj2){
obj1[str] = "any value"; // insert the property from obj2 to obj1
}