I am using MS ScriptControl to add scripting to my application. Everything works fine except when I have a class that is visible to the script engine that has an array. I can't access the array elements from the script.
Example C# class:
using System.Runtime.InteropServices;
namespace my_app
{
[ComVisible(true)]
public class test
{
public string this_works = "Hello";
public string[] no_workey = new string[4];
public test()
{
no_workey[0] = "I";
no_workey[1] = "can't";
no_workey[2] = "see";
no_workey[3] = "you!";
}
}
}
I add the class to the script engine:
script.AddObject("test", new test(), true);
I've built in some functionality to print an object. Here are my results:
test.this_works -> "Hello"
test.no_workey -> System.String[]
test.no_workey[0] -> 'test.no_workey.0' is null or not an object.
Does anyone know how I can do this?
Thanks!
Related
I want to create a new class at runtime, get the type, and create a List of that type.
For example:
void CreateNewClass(string name)
{
File file = CreateFile(name);
Type type = GetTypeOfFile(file);
List<type> someList = new List<type>();
}
void CreateFile(string name)
{
//Get a template file
//copy that file
//change some names and stuff in it
//return the file
}
Type GetTypeOfFile(File file)
{
//get the class(type) of that file
// return type
}
I do not want to generate that class in memory, I need to have that class as a file.
The file creation is not a problem.
I just need to know:
How to load the file/class and get it's type
How to create a list with the loaded type
I guess something with Reflections need to happen, but after that I'm clueless.
edit:
FYI:
I don't actually want to create a List, I need this functionallity for a Unity Project.
For those who are familiar with it:
I want to create ScriptableObjects (the class itself and the asset) with one button click.
So I have a base class like:
public abstract class State : ScriptableObject{}
As you can see it's going to be a StateMachine. Now I want kind of a window with a text field where I enter a name for a state and it creates a new class like:
public class Idle : State{}
And after that it creates the asset itself via:
AssetDatabase.CreateAsset<Idle>("/path/to/somewhere");
edit nearly working code
using System;
using System.IO;
using System.Linq;
using System.Text;
using AI.StateMachine.BaseClasses;
using UnityEditor;
using UnityEngine;
namespace AI.StateMachine.Editor
{
public class ActionWizard : EditorWindow
{
[MenuItem("Tools/AI/StateMachine/ActionWizard")]
static void Init()
{
// Get existing open window or if none, make a new one:
ActionWizard window = (ActionWizard) EditorWindow.GetWindow(typeof(ActionWizard));
window.Show();
}
private string actionName;
void OnGUI()
{
actionName = GUILayout.TextField(actionName);
if (GUILayout.Button("Create Action"))
{
//Get base class content
string baseClassContent =
File.ReadAllText($"{Application.dataPath}/Scripts/AI/StateMachine/BaseClasses/SM_Action.cs");
//refactor base class content to create derived class content
string newClassContent = baseClassContent
.Replace("SM_Action", actionName)
.Replace("ScriptableObject", "SM_Action")
.Replace("abstract ", "")
.Replace("namespace AI.StateMachine.BaseClasses", "namespace AI.StateMachine.Assets.Actions.Scripts")
.Replace("//", "");
//create derived class
using (FileStream fs = File.Create($"{Application.dataPath}/Scripts/AI/StateMachine/Assets/Actions/Scripts/{actionName}.cs"))
{
byte[] info = new UTF8Encoding(true).GetBytes(newClassContent);
fs.Write(info, 0, info.Length);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
//get type of created class
var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).ToList();
var inputType = allTypes.Find(type => typeof(SM_Action).IsAssignableFrom(type) && type.Name == actionName);
//null....
//if type was created before it works and finds the desired type
Debug.Log(inputType);
//create scriptable object instance of created type
// var so = ScriptableObject.CreateInstance(inputType);
//save scriptable object asset
// AssetDatabase.CreateAsset(so,"Assets/Scripts/AI/StateMachine/Actions/"+actionName + ".asset");
// AssetDatabase.Refresh();
}
}
}
}
You can create type from string using:
Type.GetType(string typeAssemblyQualifiedName);
But you need to use Type.assemblyQualifiedName for that so "Idle" may not work if you use namespaces or assemblies, other option would be to get all types in current domains
var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes());
and find your desired type on that list somehow
var inputType = allTypes.Find(type => typeof(State).IsAssignableFrom(type) && type.Name == inputName);
then we create scriptable object instance using that type
var so = ScriptableObject.CreateInstance(inputType);
I know this quesiton has already come up a lot, but I was looking through the answers and could not really find one that fits me, therefore I am asking you for help:
I have a custom class [Loadout]:
public class Loadout // used to save a players loadout on the server
{
public string name;
public float loadoutID;
public SO_Admiral admiral;
public FM_ShipSave[] shipsInTheLoadout;
public bool selectedLoadout;
public Loadout(string _name, FM_ShipSave[] _ShipSavesArray)
{
name = _name; // later this must come from an input-field
loadoutID = Random.Range(0f, 100000000f);
shipsInTheLoadout = _ShipSavesArray;
}
public Loadout(string _name)
{
name = _name;
shipsInTheLoadout = new FM_ShipSave[0];
}
}
it also includes other custom classes like:
public class FM_ShipSave
{
// this class saves ID and position in the array of the ship
public int shipID;
public int tileX;
public int tileZ;
public FM_ShipSave(UnitScript _unit)
{
shipID = _unit.SO_ofThis.getPositioninAllUnitsArray();
tileX = _unit.getTileX;
tileZ = _unit.getTileZ;
}
}
that I serialize into a .Json-file to send it to a server and store it there. The serialization part works perfectly:
var request = new UpdateUserDataRequest // after what is supposed to happen with the current loadout is determined we upload it to the server
{
Data = new Dictionary<string, string> // create a new dicitonary
{
{"Loadouts", JsonConvert.SerializeObject(playerLoadouts)} // convert the List<Loadout> playerLoadouts into a .json file
}
};
PlayFabClientAPI.UpdateUserData(request, onDataSend, OnError); // then send it to the server, to be stored
Link to picture of Json-editor
but as soon as I try to deserialize it:
List<Loadout> playerLoadouts = JsonConvert.DeserializeObject<List<Loadout>>(_result.Data["Loadouts"].Value); // turn the jsonback into a List<Loadout>
I am getting the following error:
Blockquote
JsonSerializationException: Unable to find a constructor to use for type Loadout. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path '[0].name', line 1, position 9.
I have also tried it with a different simpler class and there everything works fine:
public class JsonTest
{
public string name;
public JsonTest(string _name)
{
name = _name;
}
}
Send:
// to test serialization
JsonTest _test = new JsonTest("test");
var request2 = new UpdateUserDataRequest // after what is supposed to happen with the current loadout is determined we upload it to the server
{
Data = new Dictionary<string, string> // create a new dicitonary
{
{"test", JsonConvert.SerializeObject(_test)} // convert the List<Loadout> playerLoadouts into a .json file
}
};
Retrieve:
public void testDeserialization(GetUserDataResult _result)
{
JsonTest _retrievedTest = JsonConvert.DeserializeObject<JsonTest>(_result.Data["test"].Value); // turn the jsonback into a List<Loadout>
Debug.Log("retrieved test deserialization name: " + _retrievedTest.name);
}
I therefore conclude that it has something to do with my custom classes, but I don't know how to fix it. Should I have to write a custom deserializer? Or is there maybe a different solution?
I was following this tutorial to create and upload json-files:
Link to tutorial on youtube
I am coding for unity on a mac using visual studio.
Thank you for your help.
The exception message is self explaining. You don't have a default constructor for the class Loadout. So in your Loadout class you need to add
Loadout() {}
How to passing gdscript variable values to c # and vice versa.
I have nothing else to say
This worked for me.
Node setup:
CS code:
using Godot;
using System;
public class Node : Godot.Node
{
public int b = 2;
public override void _Ready()
{
GD.Print(GetTree().Root.GetChild(0).Get("a"));
}
}
GD script code:
extends Node2D
var a = 2
func _ready():
print(get_child(0).get("b"))
Just make sure that the variables are of the same type (int in this case).
I can't find this addressed on SO. I am porting a Windows .NET application, that compiles and runs perfectly, to Mono on Linux. I have missed something small. Here is a part of the code:
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace DAPTimeClock
{
public static class DatabaseOps
{
private static string _dbFile;
public static string Get_dbFile()
{
return _dbFile;
}
public void Set_dbFile(string value)
{
this._dbFile = value;
}
public static bool foundFile = false;
public static string SetFile(String dblocation = #"Data" +
"Source=../db/TimeClock.db; Version=3;")
{
Set_dbFile( dblocation );
int pathStart = Get_dbFile().IndexOf ('=') + 1;
int pathEnd = Get_dbFile().IndexOf (";") - 1;
string filePath = Get_dbFile().Substring (pathStart,
pathEnd - pathStart + 1);
if (File.Exists (filePath)) {
foundFile = true;
return string.Format ("The db file {0} has been " +
"located", filePath);
} else
{
foundFile = false;
return string.Format("Unable to find db file {0}.",
filePath);
}
}
public static bool AddPerson(Person p)
{
bool result = false;
Class Continues .....
Here are the issues:
Other classes can't find any methods from this class. ex. DatabaseOp.Set_dbFile() Yields no such method.
The methods in this class can't find methods from this class.
The methods in this class can't see the outside classes.
If I pass an argument to the methods in the class, the complier says it can't find a parameter by that name.
The methods in the class can't see its own member variables.
I have done the following:
I removed the static, now making regular objects. (no change)
I double checked the namespaces. (all the same, no typos)
I tried making the member variable public and adding a get; and set;
I am stumped. Can anyone see what might be hanging this up? Is there something about mono I missed? All other classes are working fine.
Set_dbFile should be declared as
public static void Set_dbFile(string value)
compiler should complain: "cannot declare instance members in static class". Have you miss this?
I have developed the following class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ESROWCFData
{
public class ESROWFCData
{
public string GetNCAMSData()
{
//int iOffset = 0, iTake = 0;
string strOffset = "iOffset", strTake = "iTake";
string strNCAMS = "SELECT * FROM[HRO_REPORTS].[dbo].NCAMS WHERE DATEPART(YEAR, VisitStartDate) = DATEPART(YEAR, GetDate()) ORDER BY VisitorID OFFSET " + strOffset + " rows fetch next " + strTake + " rows only";
return strNCAMS;
}
}
}
and have included the using ESROWCFData; statement in my main program but when I try to call the GetNCAMSData() function it does not show up and I get the error that it does not exist. I am obviously doing something wrong but I cannot see what it is.
You have two options here. You can either make the method static so you can call it without creating an instance of your class by changing the signature to be:
public static string GetNCAMSData()
Then call it like this:
var result = ESROWFCData.GetNCAMSData()
Or you could create a new instance of the class and then call it off that like follows
var instance = new ESROWFCData();
var result = instance.GetNCAMSData()
You first have to create an instance of the ESROWFCData class:
ESROWFCData data = new ESROWFCData();
and then use it:
string s = data.GetNCAMSData();
First of all your namespace and class name are the same :
namespace ESROWCFData
{
public class ESROWFCData
{
...
So even if you want to use this class outside you have to use full type name :
ESROWCFData.ESROWCFData data = new ESROWCFData.ESROWCFData();
In case you're trying to use it like such :
ESROWCFData data = new ESROWCFData();
Compiler then is confused because of that namespace name.
Consider name changes on the namespace or class to resolve that issue.
More about that issue
Another issue is ( the one we do not know much about ) how are you trying to call that object's method. If you want to just use them statically, mark it as static :
namespace ESROWCFData
{
public class ESROWCFData
{
public static string GetNCAMSData()
{
...
And you can call them like so : ESWROWCFData.ESWROWCFData.GetNCAMSData()