是否可以通过名称访问属性,这是一个变量?

时间:2018-06-01 23:41:31

标签: c#

对不起,如果这被问及回答,我搜索但认为我不知道词汇来找到答案。研究过反思,但这似乎不是答案吗?我显然是个新手。我正在尝试/为新的Battletech游戏的mod做出一点贡献。

我已经拥有此Dictionary,并希望使用其键来设置属性,如下面的foreach所示。我不知道这是在编译还是运行时,我的猜测是编译时...

我将* limb *作为伪代码放入我想象它可能会起作用的方式。属性mechDef.HeadLocationLoadoutDef类型的对象,其属性CurrentInternalStructurefloat

希望有意义!

非常需要任何帮助。

public class Settings {
    public readonly Dictionary<string, bool> LimbRepair = new Dictionary<string, bool> {
        { "Head", false },
        { "LeftArm", false },
        { "RightArm", false },
        { "CenterTorso", false },
        { "LeftTorso", false },
        { "RightTorso", false },
        { "LeftLeg", false },
        { "RightLeg", false },
    };
}

MechDef mechDef = new MechDef
       (__instance.DataManager.MechDefs.Get(id), __instance.GenerateSimGameUID());

foreach (string limb in settings.LimbRepair.Keys) {
    if (!settings.LimbRepair[limb]) {                          
        mechDef.*limb*.CurrentInternalStructure = Math.Max
                    (1f, mechDef.*limb*.CurrentInternalStructure * (float)rng.NextDouble());
}

5 个答案:

答案 0 :(得分:2)

你可以用反射来做,但......

使用Reflection非常容易,你可能会在这里得到几个答案,告诉你如何,但是既然你正在编写游戏,我猜你想要尽可能好的表现而且反思总是不会给你那个。

下面是一个不需要反射的解决方案,但仍允许您使用所需的循环结构。创建对象时只需要进行一些设置,然后就可以像访问字典一样访问属性。

解决方案:使用委托字典来映射属性

首先,我们需要编写一个表示属性的实用程序类。由于属性可以是不同的类型,因此这是带有类型参数的泛型类。

class PropertyWrapper<T>
{
    private readonly Func<T> _getter;
    private readonly Action<T> _setter;

    public PropertyWrapper(Func<T> getter, Action<T> setter)
    {
        _getter = getter;
        _setter = setter;
    }

    public T Value
    {
        get
        {
            return _getter();
        }
        set
        {
            _setter(value);
        }
    }
}

这个类背后的想法是你创建它来表示你想要的任何属性,并调用它的方法来读取和设置属性。该类知道如何读取和设置属性,因为你告诉它在构造它时如何通过传递一个简短的lambda表达式来完成工作。

此实用程序允许您将表示肢体的所有属性放入字典中。然后你可以按字符串查找它们,就像你的设置一样。例如,您的MechDefinition可能如下所示:

class MechDef
{
    public Limb Head        { get; set; }
    public Limb LeftArm     { get; set; }
    public Limb RightArm    { get; set; }
    public Limb LeftTorso   { get; set; }
    public Limb RightTorso  { get; set; }
    public Limb CenterTorso { get; set; }
    public Limb RightLeg    { get; set; }
    public Limb LeftLeg     { get; set; }

    private readonly Dictionary<string, PropertyWrapper<Limb>> Properties;

    public MechDef()
    {
        Properties = new Dictionary<string, PropertyWrapper<Limb>>
        {
            {"Head",       new PropertyWrapper<Limb>( () => Head,        v => Head = v )       },
            {"LeftArm",    new PropertyWrapper<Limb>( () => LeftArm,     v => LeftArm = v )    },
            {"RightArm",   new PropertyWrapper<Limb>( () => RightArm,    v => RightArm = v )   },
            {"CenterTorso",new PropertyWrapper<Limb>( () => CenterTorso, v => CenterTorso = v )},
            {"RightTorso", new PropertyWrapper<Limb>( () => RightTorso,  v => RightTorso = v ) },
            {"LeftTorso",  new PropertyWrapper<Limb>( () => LeftTorso,   v => LeftTorso = v )  },
            {"RightLeg",   new PropertyWrapper<Limb>( () => RightLeg,    v => RightLeg = v )   },
            {"LeftLeg",    new PropertyWrapper<Limb>( () => LeftLeg,     v => LeftLeg = v )    }
        };

        foreach (var property in Properties.Values) property.Value = new Limb();
    }

    public Limb this[string name]
    {
        get
        {
            return Properties[name].Value;
        }
        set
        {
            Properties[name].Value = value;
        }
    }
}

是的,那里有一些设置,但它只在一个地方,并且它只在你实例化MechDef时执行一次。现在,您可以通过字符串访问所有肢体:

foreach (var pair in settings.LimbRepair)
{
    if (pair.Value != false) continue;
    var limb = mechDef[pair.Key];
    limb.CurrentInternalStructure = Math.Max
        (
            1.0F, 
            limb.CurrentInternalStructure * (float)rng.NextDouble()
        );
}

Link to DotNetFiddle example

答案 1 :(得分:1)

您可以创建一个DynamicObject来创建自己的动态词典See the explanation here

  

假设您要为访问提供替代语法   字典中的值,以便代替写作   sampleDictionary [&#34; Text&#34;] =&#34;示例文本&#34;,你可以写   sampleDictionary.Text =&#34;示例文本&#34;。

这是上述同一篇MSDN文章中的示例:

public class DynamicDictionary : DynamicObject
{
    // The inner dictionary
    Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public int Count
    {
        get { return dictionary.Count; }
    }

    // If you try to get a value of a property not defined 
    // in the class, this method is called.
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        // Converting the property name to lowercase so 
        // that property names become case-insensitive.
        string name = binder.Name.ToLower();

        // If the property name is found in a dictionary, set the result parameter
        // to the property value and return true. Otherwise, return false.
        return dictionary.TryGetValue(name, out result);
    }

    // If you try to set a value of a property that is not 
    // defined in the class, this method is called.
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        // Converting the property name to lowercase so that 
        // property names become case-insensitive.
        dictionary[binder.Name.ToLower()] = value;

        // You can always add a value to a dictionary, so this method always returns true.
        return true;
    }
}

这就是你如何使用DynamicDictionary:

dynamic person = new DynamicDictionary();

// Adding new dynamic properties. The TrySetMember method is called.
person.FirstName = "Ellen";
person.LastName = "Adams";

答案 2 :(得分:1)

反思是获得它的一种方式。 https://stackoverflow.com/a/1954663/83250实际上完美地回答了这个问题。然而,我会重构你的数据,所以mechDef对象是另一个字典,但如果你必须保持它像你的问题,这将是有效的:

void Main()
{
    Dictionary<string, bool> limbRepair = new Dictionary<string, bool>
    {
        { "Head", false },
        { "LeftArm", false },
        { "RightArm", false },
        // Etc.
    };

    MechDefinition mechDef = new MechDefinition();
    List<Limb> limbs = new List<Limb>();
    foreach (KeyValuePair<string, bool> limbsToRepair in limbRepair.Where(x => !x.Value))
    {
        Limb limb = mechDef.GetPropValue<Limb>(limbsToRepair.Key);
        limb.CurrentInternalStructure = 9001;
    }
}

public class MechDefinition
{
    public MechDefinition()
    {
        Head = new Limb
        {
            Id = Guid.NewGuid(),
            DateAdded = DateTime.Parse("2018-01-01"),
            Name = "Main Head",
            CurrentInternalStructure = 8675309
        };
    }
    public Guid Id { get; set; }
    public string Name { get; set; }
    public int CurrentInternalStructure { get; set; }
    public Limb Head { get; set; } = new Limb();
    public Limb LeftArm { get; set; } = new Limb();
    public Limb RightArm { get; set; } = new Limb();
    // etc...
}

public class Limb
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public DateTime DateAdded { get; set; }
    public int CurrentInternalStructure { get; set; }
    public bool IsDisabled { get; set; }
}

public static class ReflectionHelpers
{
    public static object GetPropValue(this object obj, string name)
    {
        foreach (string part in name.Split('.'))
        {
            if (obj == null) { return null; }

            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }

            obj = info.GetValue(obj, null);
        }
        return obj;
    }

    public static T GetPropValue<T>(this object obj, string name)
    {
        object retval = GetPropValue(obj, name);
        if (retval == null) { return default(T); }

        // throws InvalidCastException if types are incompatible
        return (T)retval;
    }
}

请注意,反射是一项非常昂贵的操作。如果您正在处理大量数据,那将是非常低效的。请查看https://stackoverflow.com/a/7478557/83250以获取效果概述。

同样在代码方面,我更喜欢完全远离动态和反思。当您需要访问属性属性时,Reflection有其特权,如果您没有强类型对象,则动态很好。话虽如此,C#是一种强类型语言,应尽可能对其进行处理。通过将mechDef重命名为Dictionary<string, Limb>对象或类似内容,您将获得更高效的应用。

答案 3 :(得分:1)

您始终可以创建经典且有效的if .. elseswitch 或者使用函数创建字典以更新正确的属性

public class Repair
{
    public bool Active { get; set; }
    public Action<MechDef> Update { get; set; }
}

public class Settings 
{
    public readonly Dictionary<string, Repair> LimbRepair = 
        new Dictionary<string, bool> {
        { 
            "Head", 
            new Repair { Active = false, mechDef => mechDef.Head.CurrentInternalStructure = yourFunctionForHead }
        },
        { 
            "LeftArm", 
            new Repair { Active = false, mechDef => mechDef.LeftArm.CurrentInternalStructure = yourFunctionForLeftArm }
        },
        // ... and so on
    };
}

然后在循环中,您将调用正确的更新操作,使用具有强类型和编译器帮助的优点的设置类变得更加清晰,从而防止动态运行时错误

var updates = settings.LimbRepair.Where(pair => pair.Value.Active == false)
                                 .Select(pair => pair.Value);

foreach (var repair in updates) 
{
    repair.Update();
}

答案 4 :(得分:1)

如果我理解正确,你有类似的东西:

class LocationLoadoutDef
{
    public LocationLoadoutDef()
    {
        Head = new Prop();
        LeftArm = new Prop();
        RightArm = new Prop();
        CenterTorso = new Prop();
        LeftTorso = new Prop();
        RightTorso = new Prop();
        LeftLeg = new Prop();
        RightLeg = new Prop();
    }

    public Prop Head { get; set; }
    public Prop LeftArm { get; set; }
    public Prop RightArm { get; set; }
    public Prop CenterTorso { get; set; }
    public Prop LeftTorso { get; set; }
    public Prop RightTorso { get; set; }
    public Prop LeftLeg { get; set; }
    public Prop RightLeg { get; set; }
    ...
}

class Prop
{
    public float CurrentInternalStructure { get; set; }
    ...
}

因此,您可以使用反射获取对象的类型和属性。 这是基于您的伪代码的示例:

// your instance of LocationLoadoutDef
var mechDef = new LocationLoadoutDef();

//For reflection you need obtain the type
Type mechType = mechDef.GetType();

// loop your Dictionary
foreach (string limb in LimbRepair.Keys)
{
    // If the property is false in the dictionary and the type has a property with that name
    if (!LimbRepair[limb] && mechType.GetProperties().Any(p => p.Name == limb))
    {
        // Obtain the instance of the property
        var property = mechType.GetProperty(limb).GetValue(mechDef) ;

        // Get the property type 
        Type propertyType = property.GetType();

        // If the property has a property CurrentInternalStructure
        if (propertyType.GetProperties().Any(p => p.Name == "CurrentInternalStructure"))
        {
            // Obtain the current value for CurrentInternalStructure
            var currentValue = propertyType.GetProperty("CurrentInternalStructure").GetValue(property);

            // calculate the new value (I don't know what is rng)
            var newValue = 1f ; //Math.Max(1f, (float)currentValue * (float)rng.NextDouble());

            // set de value in the property
            propertyType.GetProperty("CurrentInternalStructure").SetValue(property, newValue);
        }
    }
}