Accessing values from the string name of a class at runtime - c#

In my source code for a C# class, I want to use the values of parameters defined within some other class. I want to do it without "hardwiring" the other class name into the source code (e.g., without writing something like "class2.variable").
Rather, I want to pass the name of that other class as a character string at runtime.
I am using C# within Unity. So I want to set the name of the other class as a public string within the Inspector of Unity.
For example, consider these two separate scripts:
using UnityEngine ;
public class ScriptA : ScriptableObject {public static int fred = 5 ; }
and
using System;
using System.Reflection;
using UnityEngine;
public class ScriptB : MonoBehaviour {
object item;
private static string instance;
void Start() {
instance = "ScriptA";
item = ScriptableObject.CreateInstance(Type.GetType(instance));
Type myType = item.GetType();
foreach (FieldInfo info in myType.GetFields())
{
string infoName = info.Name; //gets the name of the field
Debug.Log (" info = " + infoName);
}
}
}
ScriptB works OK ; it accesses ScriptA just from the string "instance", as evidenced by the fact that
the name "fred" to appears in the console.
But how do I access the value of "fred" ; how do I make the number "5" appear on the console?
I have been trying for two days. I have searched extensively for an answer.
Can somebody please help?

FieldInfo has a GetValue method :
public abstract object GetValue(
object obj
)
Try:
Type myType = item.GetType();
foreach (FieldInfo info in myType.GetFields())
{
string infoName = info.Name; //gets the name of the property
Console.WriteLine(" Field Name = " + infoName +"and value = "+ info.GetValue(null));
}

Related

Generate class and get type

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);

C# properties and "tostring" method

I was trying to understand properties better and I came across this page with this example:
https://www.tutorialspoint.com/csharp/csharp_properties.htm
using System;
namespace tutorialspoint {
class Student {
private string code = "N.A";
private string name = "not known";
private int age = 0;
// Declare a Code property of type string:
public string Code {
get {
return code;
}
set {
code = value;
}
}
// Declare a Name property of type string:
public string Name {
get {
return name;
}
set {
name = value;
}
}
// Declare a Age property of type int:
public int Age {
get {
return age;
}
set {
age = value;
}
}
public override string ToString() {
return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
}
}
class ExampleDemo {
public static void Main() {
// Create a new Student object:
Student s = new Student();
// Setting code, name and the age of the student
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info: {0}", s);
//let us increase age
s.Age += 1;
Console.WriteLine("Student Info: {0}", s);
Console.ReadKey();
}
}
}
output: Student Info: Code = 001, Name = Zara, Age = 9
I don't understand how the first example is able to output the whole line written in the class "student". In the main method, we are using "s" which is an object created in the class "exampledemo". How is it able to call a method from another class?
I guess it's something related to inheritance and polymorphism (I googled the override keyword) but it seems to me that the two classes are indipendent and not a subclass of the other.
I'm a total beginner at programming and probably quite confused.
s is of type Student (as declared on the first line of Main()). Therefore one can call a method on the object to modify it or print it. When you do s.Name = "Zara"; you are already calling a method on Student to update it (technically, a method and a property are the same, they only differ by syntax).
The line Console.WriteLine("Student Info: {0}", s); is actually the same as Console.WriteLine("Student Info: " + s.ToString());. The compiler allows writing this in a shorter form, but internally the same thing happens.
Let me show a real life example.
You have a sketch of your dream bicycle. Your bicycle exists only in sketch. This is a class.
Then you are going to garage and building your bicycle from the sketch. This process can be called like creation of object
of bicycle at factory from your sketch.
According to your example, class is Student.
You are creating an object by the following line:
Student s = new Student();
Object takes space in memory. How can we read values of objects from memory? By using object reference.
s is an object reference to the newly created object type of Student.
How is it able to call a method from another class?
s is an object reference to the newly created object type of Student. So it can call any public method of this object.
I was trying to understand properties
Properties are evolution of getter and setter methods. Looking for a short & simple example of getters/setters in C#
The compiler generates a pair of get and set methods for a property, plus a private backing field for an auto-implemented property.
Are C# properties actually Methods?

C# Loop trough every property in class

I've made a class containing everything from the json file.
Now I want to loop trough every object within this object so I can for fill the application with those values.
I want to scroll trough every CM0xx and use that description:
I hope my goal is clear with that screenshot.
I know that I have to do something like
foreach(CM0XX step in Stepsss)
but that simply wont work.
If making a list<string[]> for it is easier from a json but im clueless for the solution.
this is the code I have now. And the json file ive converted is generated so that should be fine.
string testdata = File.ReadAllText(Application.StartupPath + "\\TestData\\SSH.json");
Root JsonDecoded = JsonConvert.DeserializeObject<Root>(testdata);
testSteps Stepsss = JsonDecoded.SmartShoulder.TestSteps;
foreach (testSteps step in Stepsss)
{
}
this is part of the json. there are alot more CM012
public class CM181
{
public string Name;
public string description;
public string minValue;
public string maxValue;
public string unit;
public string instructions;
public string prerequisites;
}
public class Root
{
public kkkk kk;
}
public class kkkk
{
public string preset;
public testSteps TestSteps;
}
public class testSteps
{
public CM011 CM011;
}
You could use reflection to loop through the names and the values. If you just need the items under TestSteps, this should work. If you need a full hierarchy, that is a bit more involved. Here is an example below and the fiddle to it:
using System;
using System.Reflection;
public class Program
{
public static void Main()
{
var record = new Root()
{
Foo = "foo",
Bar = "bar"
};
PropertyInfo[] rootProperties = typeof(Record).GetProperties();
foreach (PropertyInfo property in rootProperties)
{
var value = property.GetValue(record);
Console.WriteLine(property.Name + " - " + value);
}
}
}
public class Root
{
public string Foo {get;set;}
public string Bar {get;set;}
}
Making a class to deserialize to is generally done to avoid the need to loop through all the properties. You've defined all the properties in the class, so you can just use them. It's a lot of overhead to use reflection to put all the values into a class, then again using reflection to pull them back out. That's why Newtonsoft has JToken/JObject/JArray classes.
using Newtonsoft.Json.Linq;
...
// parse your JSON
var jo = JObject.Parse(jsonString);
// go through all properties
// just for an example, I put them all in a dictionary
var dict = new Dictionary<String, JToken>()
foreach( JProperty p in jo.Properties() ) {
dict.Add(p.Name, p.Value)
}
Now, each Value is a JToken, which could actually be another JObject, or a JArray or just a simple token. You can check JToken.Type to see what type the property really has, and do something logical accordingly.

Can I change this extension method to remove the "magic string"? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding the Variable Name passed to a Function in C#
The class below contains the field city.
I need to dynamically determine the field's name as it is typed in the class declaration
i.e. I need to get the string "city" from an instance of the object city.
I have tried to do this by examining its Type in DoSomething() but can't find it when examining the contents of the Type in the debugger.
Is it possible?
public class Person
{
public string city = "New York";
public Person()
{
}
public void DoSomething()
{
Type t = city.GetType();
string field_name = t.SomeUnkownFunction();
//would return the string "city" if it existed!
}
}
Some people in their answers below have asked me why I want to do this.
Here's why.
In my real world situation, there is a custom attribute above city.
[MyCustomAttribute("param1", "param2", etc)]
public string city = "New York";
I need this attribute in other code.
To get the attribute, I use reflection.
And in the reflection code I need to type the string "city"
MyCustomAttribute attr;
Type t = typeof(Person);
foreach (FieldInfo field in t.GetFields())
{
if (field.Name == "city")
{
//do stuff when we find the field that has the attribute we need
}
}
Now this isn't type safe.
If I changed the variable "city" to "workCity" in my field declaration in Person this line would fail unless I knew to update the string
if (field.Name == "workCity")
//I have to make this change in another file for this to still work, yuk!
{
}
So I am trying to find some way to pass the string to this code without physically typing it.
Yes, I could declare it as a string constant in Person (or something like that) but that would still be typing it twice.
Phew! That was tough to explain!!
Thanks
Thanks to all who answered this * a lot*. It sent me on a new path to better understand lambda expressions. And it created a new question.
Maybe you need this. Works fine.
I found this here.
static void Main(string[] args)
{
var domain = "matrix";
Check(() => domain);
Console.ReadLine();
}
static void Check<T>(Expression<Func<T>> expr)
{
var body = ((MemberExpression)expr.Body);
Console.WriteLine("Name is: {0}", body.Member.Name);
Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
.GetValue(((ConstantExpression)body.Expression).Value));
}
Output will be:
Name is: 'domain'
Value is: 'matrix'
I know this is old question, but I was trying to achieve the same and google sent me here. After many hours I finally found a way. I hope somebody else will find this useful.
There are actually more ways to accomplish this:
static void Main(string[] args)
{
GetName(new { var1 });
GetName2(() => var1);
GetName3(() => var1);
}
static string GetName<T>(T item) where T : class
{
return typeof(T).GetProperties()[0].Name;
}
static string GetName2<T>(Expression<Func<T>> expr)
{
return ((MemberExpression)expr.Body).Member.Name;
}
static string GetName3<T>(Func<T> expr)
{
return expr.Target.GetType().Module.ResolveField(BitConverter.ToInt32(expr.Method.GetMethodBody().GetILAsByteArray(), 2)).Name;
}
The first is fastest. The last 2 are approx 20 times slower than the 1st one.
http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html
city in this case is an instance of type string. When you call .GetType() you return the actual string type, which has no knowledge at all of your particular city instance.
I'm having a hard time seeing why you can't just type "city" in the code as a string literal here, if that's what you need. Perhaps it would help if you shared what you want to use this value for and in what circumstances you will call your DoSomething() function.
At the moment, my best guess is that what you really want to do is reflect the entire Person class to get a list of the fields in that class:
public void DoSomething()
{
MemberInfo[] members = this.GetType().GetMembers();
// now you can do whatever you want with each of the members,
// including checking their .Name properties.
}
Okay, based on your edit I have some more for you.
You can find the name of fields that are decorated with your attribute at run-time like this:
Type t = typeof(Person);
foreach (MemberInfo member in t.GetMembers()
.Where(m =>
m.GetCustomAttributes(typeof(MyCustomAttribute)).Any() ) )
{
// "member" is a MemberInfo object for a Peson member that is
// decorated with your attribute
}
You can also use binding flags in the first GetMembers() call to limit it to just fields, if you want.
You mentioned "i.e. I need to get the string "city" from an instance of the object city."
Are you looking to get the field name from the value of the field.
For example:If there are 2 Person object one with city "New York" and the other with city "London", are you looking for the function to return "city". Is this what you mean by dynamic?
With your current design you will always need to compare the name of the field from the FieldInfo against a string.
What if you instead decouple this so that you hold the identifier to use for comparison purposes during reflection as part of the attribute.
Something like this:
public enum ReflectionFields
{
CITY = 0,
STATE,
ZIP,
COUNTRY
}
[AttributeUsage(AttributeTargets.Field,AllowMultiple=false)]
public class CustomFieldAttr : Attribute
{
public ReflectionFields Field { get; private set; }
public string MiscInfo { get; private set; }
public CustomFieldAttr(ReflectionFields field, string miscInfo)
{
Field = field;
MiscInfo = miscInfo;
}
}
public class Person
{
[CustomFieldAttr(ReflectionFields.CITY, "This is the primary city")]
public string _city = "New York";
public Person()
{
}
public Person(string city)
{
_city = city;
}
}
public static class AttributeReader<T> where T:class
{
public static void Read(T t)
{
//get all fields which have the "CustomFieldAttribute applied to it"
var fields = t.GetType().GetFields().Where(f => f.GetCustomAttributes(typeof(CustomFieldAttr), true).Length == 1);
foreach (var field in fields)
{
var attr = field.GetCustomAttributes(typeof(CustomFieldAttr), true).First() as CustomFieldAttr;
if (attr.Field == ReflectionFields.CITY)
{
//You have the field and you know its the City,do whatever processing you need.
Console.WriteLine(field.Name);
}
}
}
}
public class Program
{
public static void Main(string[] args)
{
PPerson p1 = new PPerson("NewYork");
PPerson p2 = new PPerson("London");
AttributeReader<PPerson>.Read(p1);
AttributeReader<PPerson>.Read(p2);
}
}
You can now freely rename _city field of Person to something else and your calling code will still work since the code using reflection is trying to identify the field using the ReflectionFields enum value set as part of initialization of the attribute set on the field.
Yes its possible !!!
Try this out...
public string DoSomething(object city)
{
return city.GetType().GetProperty("Name",typeof(string)).GetValue(city,null);
}
Two things here.
Number one, as someone above pointed out, you're getting the Type for string, not for Person. So typeof(Person).GetMembers() will get you the list of members.
Number two, and more importantly, it looks like you're misunderstanding the purpose of attributes. In general attributes are used to mark a member for specific processing or to add additional information. Here you're using the name to indicate what processing you want, and the attribute to specify parameters, which is mixing of metaphors, or something.
Abhijeet's answer is more appropriate, you mark the field as a city field, then do what you like with it. Where I disagree is that I would use different attribute classes, rather than an enumeration.
Something like:
public class MyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Field)]
public class MyCityAttribute : MyAttribute
{
}
[AttributeUsage(AttributeTargets.Field]
public class MyNameAttribute: MyAttribute
{
}
public class Person
{
[MyCity]
public string city = "New York";
[MyCity]
public string workCity = "Chicago";
[MyName]
public string fullName = "John Doe";
public Person()
{
}
public void DoSomething()
{
Type t = typeof(Person);
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
MyAttribute[] attributes = field.GetCustomAttributes(typeof(MyAttribute));
if (attributes.Count > 0)
{
if (attributes[0] is MyCityAttribute)
{
//Dosomething for city
break;
}
if (attributes[0] is MyNameAttribute)
{
//Dosomething for names
break;
}
}
}
}
}
This would allow you to use different parameters for MyCity vs MyName that would make more sense in the context of processing each.
I think with your 'yuk' comment above, you hit the nail on the head. That you would have to change a string constant if you rename your variable is an indicator that you're doing something wrong.
t.GetField("city", BindingFlags.Public | BindingFlags.Instance);
or you can call GetFields() to get all fields
You need to call get type on the class Person. The iterate the fields of the class as in the answer below
This is not possible (I think it actually is but involes several hacks and using lambdas). If you want to store attributes about a Person and be able to get the name of the attribute easily, I suggest using a Dictionary<TKey, TValue> from the System.Collections.Generic namespace.
And you can always make public properties that wrap the dictionary.
public class Person
{
Dictionary<string, string> attributes = new Dictionary<string, string();
public string City
{
get { return attributes["city"]; }
set { attributes["city"] = value; }
}
public Person()
{
City = "New York";
}
}
And you can get a list of all attributes with attributes.Keys.
Have a look at this post as it looks similar to what you're trying to do:
Finding the variable name passed to a function
(especially Konrad Rudolph's answer) Another approach could be to just add "city" as one of the parameters in the attribute and fish that out later.
You are already looping through the collection of FieldInfo objects. Look for your attribute on those and when you find the FieldInfo that contains your attribute, you have the one you want. Then call .Name on it.
system.reflection.fieldinfo.attributes

Cant call my method on object from arraylist

I'm calling getName() method from Achiv class on an ArrayList in printAchiv() method located in Achievements script and it throws an error.
Here is the error message that I get on the line Debug.Log("Achiv "+i+": "+ achivList[i].getName());:
Type object' does not contain a definition for getName' and no
extension method getName' of type object' could be found (are you
missing a using directive or an assembly reference?)
I'm just trying to access value of var "name" from obejct in collection.
Achiv class :
using UnityEngine;
using System.Collections;
public class Achiv: MonoBehaviour
{
public string name;
public Achiv(string name)
{
this.name = name;
}
public string getName()
{
return name;
}
}
Achievements script :
using UnityEngine;
using System.Collections;
public class Achievements: MonoBehaviour
{
public ArrayList achivList = new ArrayList();
void Start()
{
achivList.Add(
new Achiv("First name", "Descirptionn", false));
achivList.Add(
new Achiv("Second name", "Descirptionnn", false));
printAchiv();
}
void printAchiv()
{
for (int i = 0; i <= achivList.Count - 1; i++)
Debug.Log("Achiv " + i + ": " + achivList[i].getName());
}
}
Use List<Achiv> instead of ArrayList. ArrayList is archaic, not type safe shouldn't be used anymore.
Indexer of ArrayList returns object that's why you get the error. Try the following.
public List<Achiv> achivList = new List<Achiv>();
Apart from this,
Don't expose List publicly, prefer ReadOnlyCollection or IEnumerable.
Prefer foreach over for unless there is a good reason.
printAchiv doesn't follow proper naming convention, In c# we use "CamelCase", Rename it to PrintAchiv.
get/set methods are for java style languages which doesn't supports properties. In c# we use properties instead. Create a property namely Name.
The problem is that the elements inside the ArrayList are stored as object. Thus achivList[i] returns an object, which does not provide the getName() method.
Either you can add a cast:
Debug.Log("Achiv "+i+": "+ (Achiv)achivList[i].getName());
or you can switch to a generic List:
using UnityEngine;
using System.Collections.Generic;
public class Achievements: MonoBehaviour {
public List<Achiv> achivList = new List<Achiv>();
void Start () {
achivList.Add (new Achiv("First name", "Descirptionn", false));
achivList.Add (new Achiv("Second name", "Descirptionnn", false));
printAchiv();
}
void printAchiv(){
for (int i = 0; i <= achivList.Count - 1; i++)
{
Debug.Log("Achiv "+i+": "+ achivList[i].getName());
}
}
}
ArrayList operates with Objects. You need to cast result of array indexing to Achiv:
(achivList[i] as Achiv).getName()

Categories