首页 > 解决方案 > 将对象列表转换为枚举条目c#

问题描述

如何将 Class 对象列表传递给枚举?

目前我有一个从 XML 文件中提取数据的类结构。这是他们的样子:

[XmlRoot("ManufacturerContainer")]
public class ManufacturerContainer
{
    [XmlArray("Manufacturers")]
    [XmlArrayItem("Manufacturer")]
    public List<Manufacturer> Manufacturers { get; set; }
}

public class Manufacturer
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlElement("Model")]
    public List<Model> Models { get; set; }    
}

public class Model
{
    [XmlAttribute("name")]
    public string Name { get; set; }

我要做的是创建 2 个枚举下拉框,模型下拉框取决于制造商。您将选择制造商,模型将更新。您如何动态更改枚举?

谢谢!

标签: c#xmlunity3d

解决方案


不能/不应该尝试enum在运行时更改。用简单的话说它enum有点像一个constant你不能在运行时改变的字典。

即使可以,任何代码(例如 switch-case 或任何取决于该枚举的 if-else 都可能失败)。


没有简单快速的解决方案。对于您想要做的事情,您必须深入了解 Unity 编辑器脚本并使用as 选项(例如此处的名称)创建您的自定义Popup和string[]

首先,我会为您的类添加一些 getter 方法(所有这些都假设Name您的示例中的 s 是唯一的,并且一个制造商中的制造商或型号都没有相同的名称):

[XmlRoot("ManufacturerContainer")]
public class ManufacturerContainer
{
    [XmlArray("Manufacturers")]
    [XmlArrayItem("Manufacturer")]
    public List<Manufacturer> Manufacturers { get; set; }

    // Get all available Manufacturers' names
    public IEnumerable<string> GetAvailableManufacturers()
    {
        // use Linq to get a list of only the names
        return Manufacturers.Select(manufacturer => manufacturer.Name);
    }

    // Get a specific Manufacturer by name
    public Manufacturer GetManufacturerByName(string name)
    {
        // a little sanaty check
        if (GetAvailableManufacturers().Contains(name))
        {
            // again use Linq to return the Manufacturer by name
            // (provided that they are unique of course)
            return Manufacturers.Find(manufacturer => string.Equals(name, manufacturer.Name));
        }

        Debug.LogWarningFormat("No Manufacturer with name {0} found!", name);
        return null;
    }

    // and finally get a specific Model by manufacturer and model name
    public Model GetModelByName(string manufacturerId, string modelId)
    {
        // first get the according manufacturer
        var manufacturer = GetManufacturerByName(manufacturerId);

        if (manufacturer != null)
        {
            // get the model by name (see method below)
            return manufacturer.GetModelByName(modelId);
        }

        Debug.LogWarningFormat("Couldn't get Manufacturer with id {0}", manufacturerId);
        return null;

        // then the according model
    }
}

public class Manufacturer
{
    [XmlAttribute("name")] public string Name { get; set; }

    [XmlElement("Model")] public List<Model> Models { get; set; }

    // Get all available Models' names
    public List<string> GetAvailableModels()
    {
        return Models.Select(model => model.Name).ToList();
    }

    // The same as for getting a Manufacturer by name
    public Model GetModelByName(string name)
    {
        // a little sanaty check
        if (GetAvailableModels().Contains(name))
        {
            // again use Linq to return the Model by name
            // (provided that they are unique of course)
            return Models.Find(model => string.Equals(name, model.Name));
        }

        Debug.LogWarningFormat("No Manufacturer with name {0} found!", name);
        return null;
    }
}

public class Model
{
    [XmlAttribute("name")] public string Name { get; set; }
}

所以现在你可以

  • 获取制造商及其型号的所有可用选项
  • 稍后通过 ID 查找特定的制造商/型号(例如,如果您需要有关它们的更多信息等)

因此,下一步是根据您的需要为您的类编写CustomEditorCustomPropertyDrawer 。

我将为您提供一个关于 PropertyDrawer 外观的非常简化的想法。不幸的是,编辑器脚本总是变得庞大,并且您必须手动完成很多事情:

[CustomPropertyDrawer(typeof(ModelSelection))]
public class ModelSelectionDrawer : PropertyDrawer
{
    // Lets just say we want the property to have a height of 3 lines
    // one for the label, and one for each dropdown
    // It makes it easier for now to place the dropdowns
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUIUtility.singleLineHeight * 3;
    }

    // Kind of like the Inspectors update method
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        EditorGUI.LabelField(position, label);
        // go to next line
        position.y += EditorGUIUtility.singleLineHeight;

        // Don't make child fields be indented
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;


        // first of all link the according variables
        var _manufacturerId = property.FindPropertyRelative("ManufacturerId");
        var _modelId = property.FindPropertyRelative("ModelId");

        // You somehow need access to the reference of ManufacturerContainer
        // I will assume here you now how to make it accessable all over your scene 
        // and pretend it is already stored in this variable

        // I just create one right here in order to have something to show already
        var container = new ManufacturerContainer
        {
            Manufacturers = new List<Manufacturer>
            {
                new Manufacturer
                {
                    Name = "Man_1",
                    Models = new List<Model>
                    {
                        new Model
                        {
                            Name = "Model_A"
                        },
                        new Model
                        {
                            Name = "Model_B"
                        }
                    }
                },

                new Manufacturer
                {
                    Name = "Man_2",
                    Models = new List<Model>
                    {
                         new Model
                         {
                             Name = "Model_C"
                         },
                         new Model
                         {
                             Name = "Model_D"
                         }
                    }
                }
            }
        };

        // Now you would get the available manufacturers
        var availableManufacturers = container.GetAvailableManufacturers().ToArray();

        // Make the dropdown field for manufacturer
        // Get the position for this field
        var field1_rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
        // add a label
        field1_rect = EditorGUI.PrefixLabel(field1_rect, new GUIContent("Manufacturer"));
        position.y += EditorGUIUtility.singleLineHeight;

        // get index of currently selected manufacturer
        var currentManIndex = availableManufacturers.ToList().IndexOf(_manufacturerId.stringValue);
        // clamp to minimum 0 (it returns -1 if the string is incorrect/not found in the list)
        currentManIndex = Mathf.Max(currentManIndex, 0);

        // problem it returns an int -> index
        // => if you add or delete an item later in middle the XML it breaks
        // you would have to fix this on your own
        var newManIndex = EditorGUI.Popup(field1_rect, currentManIndex, availableManufacturers.ToArray());

        // now we have the new index but we have to write back the selected object to the target class
        _manufacturerId.stringValue = availableManufacturers[newManIndex];



        // Now that you have a manufacturer selected you can get and select the model
        var availableModels = container.GetManufacturerByName(_manufacturerId.stringValue).GetAvailableModels().ToArray();

        // Make the dropdown field for manufacturer
        // Get the position for this field
        var field2_rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
        // add a label
        field2_rect = EditorGUI.PrefixLabel(field2_rect, new GUIContent("Model"));
        position.y += EditorGUIUtility.singleLineHeight;

        // get index of currently selected manufacturer
        var currentModIndex = availableModels.ToList().IndexOf(_modelId.stringValue);
        // also clamp this to minimum 0
        currentModIndex = Mathf.Max(currentModIndex, 0);


        // problem it returns an int -> index
        // => if you add or delete an item later in middle the XML it breaks
        // you would have to fix this on your own
        var newModIndex = EditorGUI.Popup(field2_rect, currentModIndex, availableModels.ToArray());

        // now we have the new index but we have to write back the selected object to the target class
        _modelId.stringValue = availableModels[newModIndex];

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

如前所述,这只是一个示例,肯定还有更多的检查和应该添加的东西。但是您稍后会在任何类中使用此属性,例如

public class Blarf : MonoBehaviour
{
    public ModelSelection selection;
}

它在编辑器中看起来像这样:

在此处输入图像描述

其余的取决于选择这两个值后您的目标是什么。例如,您可能只是想获得相应的Model使用

// reference to your container
container.GetModelByName(selection.ManufacturerId, selection.ModelId);

推荐阅读