![](/img/trans.png)
[英]How can I take all the elements / fields from a Listbox (in C#) and put them into a DataTable?
[英]UNITY, How can I get all properties from another C# class and put them into an enum
我有两节课:
public class Stats : MonoBehaviour
{
// Primary Stats
public int strength;
public int agility;
public int intellect;
public int stamina;
public int spirit;
}
和
public class EquipmentProperties : ItemProperties
{
public Stats stats;
}
public enum Stats
{//variables from "Stats" class to be in this enum
}
我正在尝试从 Stats class 中获取所有变量,而无需手动输入它们。
“我正在尝试从 Stats class 中获取所有变量,而无需手动输入它们”
枚举必须在编译时指定,您不能在运行时动态添加枚举。 如果您想使用 class 变量动态建立枚举字段,请猜测因为Stats
class 可能会随着应用程序的开发而改变,您需要将该枚举存储在某处,因为如果不是,您需要访问以下字段动态枚举根据设置枚举的通用方式,以一种没有多大意义的元编程模板方式。
因此,随着您的问题而来的是我猜如何存储该枚举以供以后使用的问题。 为此,您可以检查EnumBuilder class 。
扩展该示例,您可以根据特定的Stats
class 构建枚举,如下所示:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
public class Stats
{
// Primary Stats
public int strength;
public int agility;
public int intellect;
public int stamina;
public int spirit;
}
class Example
{
public static List<string> getFields(Type type) {
var propertyValues = type.GetFields();
var result = new Stats[propertyValues.Length];
var retStr = new List<string>();
for (int i = 0; i < propertyValues.Length; i++) {
retStr.Add(propertyValues[i].Name);
}
return retStr;
}
public static void Main() {
// Get the current application domain for the current thread.
AppDomain currentDomain = AppDomain.CurrentDomain;
// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName aName = new AssemblyName("TempAssembly");
AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
aName, AssemblyBuilderAccess.RunAndSave);
// Define a dynamic module in "TempAssembly" assembly. For a single-
// module assembly, the module has the same name as the assembly.
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
// Define a public enumeration with the name "Elevation" and an
// underlying type of Integer.
EnumBuilder eb = mb.DefineEnum("Stats", TypeAttributes.Public, typeof(int));
int fieldCount = 0;
getProperties(typeof(Stats)).ForEach(field => {
eb.DefineLiteral(field, fieldCount);
fieldCount++;
});
// Define two members, "High" and "Low".
//eb.DefineLiteral("Low", 0);
//eb.DefineLiteral("High", 1);
// Create the type and save the assembly.
Type finished = eb.CreateType();
ab.Save(aName.Name + ".dll");
foreach (object o in Enum.GetValues(finished)) {
Console.WriteLine("{0}.{1} = {2}", finished, o, ((int)o));
}
Console.ReadLine();
}
}
Output:
Stats.strength = 0
Stats.agility = 1
Stats.intellect = 2
Stats.stamina = 3
Stats.spirit = 4
这当然不是您直接要求的,因为它不是自动的,但我建议使用Dictionary<Stats, int>
并执行例如
public class StatsComponent : MonoBehaviour
{
// Make these only assignable via the Inspector
[SerializeField] private int strength;
[SerializeField] private int agility;
[SerializeField] private int intellect;
[SerializeField] private int stamina;
[SerializeField] private int spirit;
public readonly Dictionary<Stats, int> stats = new Dictionary<Stats, int>();
private void Awake ()
{
// Initialize once with values from the Inspector
stats.Add(Stats.Strength, strength);
stats.Add(Stats.Agility, agility);
stats.Add(Stats.Intellect, intellect);
stats.Add(Stats.Stamina, stamina);
stats.Add(Stats.Spirit, spirit);
}
}
public enum Stats
{
Strength,
Agility,
Intellect,
Stamina,
Spirit
}
当然,有一些方法可以通过反射实现自动化,但我相信它会给你带来更多的头痛和问题,然后它正在解决——这当然只是一种观点。
如果您不想输入两次内容,则可以通过索引或字符串代替枚举而不是 go,例如使用SerializedDictionary
您可以简单地拥有一个
public SerializedDictionary<string, int> stats;
并将其填写在 Inspector 中,并且根本没有您的字段。
但是,如果您仍然希望在此答案之上以最少的努力实现自动化,我将其作为一个工具 EditorWindow,您可以直接在 Unity 中使用。
只需将此脚本放在项目中的任何位置即可。
#if UNITY_EDITOR
using System;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public class EnumGeneratorWindow : EditorWindow
{
// This is of course optional but I thought it makes sense to filer for a specific field type
private enum FieldType
{
Int,
Float,
Bool,
// more could of course be added
}
private MonoScript sourceScript;
private MonoScript targetScript;
private FieldType fieldType;
private Type GetFieldType()
{
return fieldType switch
{
FieldType.Int => typeof(int),
FieldType.Float => typeof(float),
FieldType.Bool => typeof(bool),
// according to the enum add more cases
_ => null
};
}
[MenuItem("Window/ENUM GENERATOR")]
private static void Init()
{
var window = GetWindow<EnumGeneratorWindow>();
window.Show();
}
private void OnGUI()
{
EditorGUILayout.LabelField("ENUM GENERATOR", EditorStyles.boldLabel);
sourceScript = EditorGUILayout.ObjectField("Source", sourceScript, typeof(MonoScript), false) as MonoScript;
if (!sourceScript)
{
EditorGUILayout.HelpBox("Reference the script where to fetch the fields from", MessageType.None, true);
return;
}
var sourceType = sourceScript.GetClass();
if (sourceType == null)
{
EditorGUILayout.HelpBox("Could not get Type from source file!", MessageType.Error, true);
return;
}
targetScript = EditorGUILayout.ObjectField("Target", targetScript, typeof(MonoScript), false) as MonoScript;
if (!targetScript)
{
EditorGUILayout.HelpBox("Reference the script where write the generated enum to", MessageType.None, true);
return;
}
if (targetScript == sourceScript)
{
EditorGUILayout.HelpBox("The source and target script should probably rather not be the same file ;)", MessageType.Error, true);
return;
}
var targetType = targetScript.GetClass();
if (targetType == null)
{
EditorGUILayout.HelpBox("Could not get Type from target file!", MessageType.Error, true);
return;
}
fieldType = (FieldType)EditorGUILayout.EnumPopup("Field Type", fieldType);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel);
var fields = sourceType.GetFields().Where(f => f.FieldType == GetFieldType()).Select(f => f.Name).ToArray();
var fileContent = new StringBuilder("public enum ").Append(targetType.Name).Append(" { ");
for (var i = 0; i < fields.Length; i++)
{
if (i != 0)
{
fileContent.Append(", ");
}
fileContent.Append(fields[i]);
}
fileContent.Append(" }");
EditorGUILayout.LabelField(fileContent.ToString());
var color = GUI.color;
GUI.color = Color.red;
GUILayout.BeginVertical();
{
EditorGUILayout.LabelField("! DANGER ZONE !", EditorStyles.boldLabel);
EditorGUILayout.Space();
if (GUILayout.Button("GENERATE ENUM"))
{
var targetID = targetScript.GetInstanceID();
// e.g. Assets/SomeFolder/MyStats.cs
var targetAssetPath = AssetDatabase.GetAssetPath(targetID);
// just as a safety net
if (EditorUtility.DisplayDialog("Generate and overwrite with enum?", $"Attention\n\nThis will overwrite any content of {targetAssetPath} with the new content.\n\nAre you sure?", "Yes generate", "OMG NO! Cancel this!"))
{
// a bit of a hack but we need to convert the Unity asset path into a valid system path by erasing one duplicate "Assets"
var pathParts = targetAssetPath.Split('/').ToArray();
// overwrite the "Assets" with the full path to Assets
pathParts[0] = Application.dataPath;
// re-combine all path parts but this time use the according system file path separator char
var targetSystemPath = Path.Combine(pathParts);
// Write the content into the file via the normal file IO
File.WriteAllText(targetSystemPath, fileContent.ToString());
// trigger a refresh so unity re-loads and re-compiles
AssetDatabase.Refresh();
}
}
}
GUILayout.EndVertical();
GUI.color = color;
}
}
#endif
这个怎么运作:
MonoBehaviour
脚本拖入“Source”这是一个小演示;)
我刚开始使用Example.cs
public class Example : MonoBehaviour
{
public int someInt, anotherInt;
public float someFloat, anotherFloat, andOnMore;
public bool someBool, yetAnotherBool;
}
和ExampleEnum.cs
public enum ExampleEnum
{
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.