簡體   English   中英

使用反射在嵌套對象中設置屬性

[英]Set property in nested object using reflection

我正在嘗試使用反射在obj1中設置Address1,我無法弄清楚如何獲得對正確對象的引用。 我不知道如何獲取對Address1實例的引用以傳遞給SetValue()的第一個參數

第1輪:

public class StackOverflowReflectionTest
    {
        [Fact]
        public void SetDeepPropertyUsingReflection()
        {
            var breadCrumb = ".Addresses[0].Address1";

            var obj1 = new Person()
            {
                Name = "Eric",
                Addresses = new List<Address>()
                {
                    new Address() {Address1 = "123 First Street"}
                }
            };

            var newAddress1 = "123 Second Street";

            var propNames = breadCrumb.Split(".");
            for (var index = 0; index < propNames.Length; index++)
            {
                var propName = propNames[index];
                if (propName.Contains("["))
                {
                    var propNameToGet = propName.Substring(0, propName.IndexOf("[", StringComparison.Ordinal));
                    var prop = obj1.GetType().GetProperty(propNameToGet);
                    var leftBrace = propName.IndexOf("[", StringComparison.Ordinal);
                    var rightBrace = propName.IndexOf("]", StringComparison.Ordinal);
                    var position = int.Parse(propName.Substring(leftBrace + 1, rightBrace - leftBrace - 1));

                    var propNameToSet = propNames[index + 1];


                    var propToSet = prop.PropertyType.GetGenericArguments()[position].GetProperty(propNameToSet);
                    propToSet.SetValue(obj1, newAddress1);
                }
                else
                {
                    //TODO: deal with different types
                }
            }
        }

        public class Person
        {
            public string Name { get; set; }
            public IList<Address> Addresses { get; set; }
        }

        public class Address
        {
           public string Address1 { get; set; }
        }
    }

第2輪基於Ed的反饋,仍然堅持如何獲得該行的值:var value = property.GetValue(obj,new object [] {indexPart});

  public class StackOverflowReflectionTest
    {
        [Fact]
        public void SetDeepPropertyUsingReflectionRound2()
        {
            var breadCrumb = "Addresses[0].Address1";

            var obj1 = new Person()
            {
                Name = "Eric",
                Addresses = new List<Address>()
                {
                    new Address() {Address1 = "123 First Street"}
                }
            };

            var newAddress1 = "123 Second Street";

            SetPropertyValueByPath(obj1, breadCrumb, newAddress1);
        }

        public bool CrackPropertyName(string name, out string namePart, out object indexPart)
        {
            if (name.Contains("["))
            {
                namePart = name.Substring(0, name.IndexOf("[", StringComparison.Ordinal));

                var leftBrace = name.IndexOf("[", StringComparison.Ordinal);
                var rightBrace = name.IndexOf("]", StringComparison.Ordinal);

                indexPart = name.Substring(leftBrace + 1, rightBrace - leftBrace - 1);
                return true;
            }
            else
            {
                namePart = name;
                indexPart = null;
                return false;
            }
        }

        public object GetPropertyValue(object obj, string name)
        {
            if(CrackPropertyName(name, out var namePart, out var indexPart))
            {
                var property = obj.GetType().GetProperty(namePart);
                var value = property.GetValue(obj, new object[] { indexPart });
                return value;
            }
            else
            {
                return obj.GetType().GetProperty(name);
            }

        }

        public void SetPropertyValue(object obj, string name, object newValue)
        {
            var property = typeof(Address).GetProperty(name);
            property.SetValue(obj, newValue);
        }

        public void SetPropertyValueByPath(object obj, string path, object newValue)
        {
            var pathSegments = path.Split(".");

            if (pathSegments.Length == 1)
            {
                SetPropertyValue(obj, pathSegments[0], newValue);
            }
            else
            {
                ////  If more than one remaining segment, recurse

                var child = GetPropertyValue(obj, pathSegments[0]);

                SetPropertyValueByPath(child, String.Join(".", pathSegments.Skip(1)), newValue);
            }
        }

        public class Person
        {
            public string Name { get; set; }
            public IList<Address> Addresses { get; set; }
        }

        public class Address
        {
           public string Address1 { get; set; }
        }
}

解:

public class StackOverflowReflectionTest
    {
[Fact]
        public void SetDeepPropertyUsingReflectionSolution()
        {
            var breadCrumb = "Addresses[0].Address1";

            var obj1 = new Person()
            {
                Name = "Eric",
                Addresses = new List<Address>()
                {
                    new Address() {Address1 = "123 First Street"}
                }
            };

            var newAddress1 = "123 Second Street";

            SetPropertyValueByPath(obj1, breadCrumb, newAddress1);
        }

        public bool CrackPropertyName(string name, out string namePart, out object indexPart)
        {
            if (name.Contains("["))
            {
                namePart = name.Substring(0, name.IndexOf("[", StringComparison.Ordinal));

                var leftBrace = name.IndexOf("[", StringComparison.Ordinal);
                var rightBrace = name.IndexOf("]", StringComparison.Ordinal);

                indexPart = name.Substring(leftBrace + 1, rightBrace - leftBrace - 1);
                return true;
            }
            else
            {
                namePart = name;
                indexPart = null;
                return false;
            }
        }

        public object GetPropertyValue(object obj, string name)
        {
            if(CrackPropertyName(name, out var namePart, out var indexPart))
            {

                var property = obj.GetType().GetProperty(namePart);
                var list = property.GetValue(obj);
                var value = list.GetType().GetProperty("Item").GetValue(list, new object[] { int.Parse(indexPart.ToString()) });
                return value;
            }
            else
            {
                return obj.GetType().GetProperty(namePart);
            }

        }

        public void SetPropertyValue(object obj, string name, object newValue)
        {
            var property = typeof(Address).GetProperty(name);
            property.SetValue(obj, newValue);
        }

        public void SetPropertyValueByPath(object obj, string path, object newValue)
        {
            var pathSegments = path.Split(".");

            if (pathSegments.Length == 1)
            {
                SetPropertyValue(obj, pathSegments[0], newValue);
            }
            else
            {
                ////  If more than one remaining segment, recurse
                var child = GetPropertyValue(obj, pathSegments[0]);

                SetPropertyValueByPath(child, String.Join(".", pathSegments.Skip(1)), newValue);
            }
        }

        public class Person
        {
            public string Name { get; set; }
            public IList<Address> Addresses { get; set; }
        }

        public class Address
        {
           public string Address1 { get; set; }
        }
}

如果要使用反射從Person實例獲取Addresses屬性的值,則執行以下操作:

 var myPerson = new Person()
        {
            Name = "Eric",
            Addresses = new List<Address>()
            {
                new Address() {Address1 = "123 First Street"}
            }
        };  
var property = typeof(Person).GetProperty("Addresses");
var addresses = (IList<Address>) property.GetValue(myPerson );

首先,您要找到屬性 - PropertyInfo一個實例 - 屬於Person類型。 然后,您將檢索Person的特定實例myPerson的該屬性的值。

addressesIList<Address>因此使用反射從列表中獲取特定Address沒有多大用處。 但如果由於某種原因你想:

private Address GetAddressAtIndex(IList<Address> addresses, int index)
{
    var property = typeof(IList<Address>).GetProperty("Item");
    var address = (Address) property.GetValue(addresses, new object []{index});
    return address;
}

這與第一個示例基本相同,只是在這種情況下屬性( Item )需要索引。 因此我們使用接受一個或多個索引的GetValue重載。

現在你有一個Address實例。 我正在分別執行這些步驟,因為它們都是單獨的步驟。 沒有一個步驟可以執行整個操作。

如果您有一個地址實例,並且您想使用反射來設置Address1屬性:

private void SetAddress1OnAddress(Address address, string address1Value)
{
    var property = typeof(Address).GetProperty("Address1");
    property.SetValue(address, address1Value);
}

非常相似。 您首先檢索Address1屬性,然后調用其SetValue方法以在特定實例(如果Address上設置值。

這是我在LINQPad中放在一起的快速和臟,它改變了你定義的對象的Address1屬性。

void Main()
{
    var obj1 = new Person()
    {
        Name = "Eric",
        Addresses = new List<Address>()
                {
                    new Address() {Address1 = "123 First Street"}
                }
    };

    var index = 0;

    var addressList = typeof(Person)
             .GetProperty("Addresses")
             .GetValue(obj1);

    var address = addressList.GetType()
             .GetProperty("Item")
             .GetValue(addressList, new object[]{index});

    address.GetType()
             .GetProperty("Address1")
             .SetValue(address,"321 Fake Street");

    Console.WriteLine(obj1.Addresses[index].Address1); // Outputs 321 Fake Street
}

// Define other methods and classes here
public class Person
{
    public string Name { get; set; }
    public IList<Address> Addresses { get; set; }
}

public class Address
{
    public string Address1 { get; set; }
}

Type.GetGenericArguments()沒有做我認為你假設的事情。

你想要的是遞歸。 鑒於”Foo.Bar[1].Baz” ,得到Foo 從那里得到Bar[1] 從其父級獲取Baz的PropertyInfo,使用它來設置FooBar[1]屬性的Baz屬性的值。

要打破它:

  1. 編寫一個“破解”屬性名稱並使用out參數返回名稱部分和索引值部分的方法: "IndexedProperty[1]"進入; “IndexedProperty”和整數1出來了。 "FooBar"進去了, "FooBar"null出來了。 如果有索引器,則返回true,否則返回false。

     bool CrackPropertyName(string name, out string namePart, out object indexPart) 
  2. 編寫一個方法,該方法接受一個對象,字符串“PropertyName”或“IndexedPropety [0]”(不是路徑 - 無點),並返回該對象上該屬性的值。 它使用CrackPropertyName()來簡化其工作。

     object GetPropertyValue(object obj, string name) 
  3. 編寫一個按名稱設置屬性值的方法(不是通過路徑,只是按名稱)。 同樣,它使用CrackPropertyName()來簡化其工作。

     void SetPropertyValue(object obj, string name, object newValue) 
  4. 使用上述的遞歸方法:

     void SetPropertyValueByPath(object obj, string path, object newvalue) { var pathSegments = /* split path on '.' */; if (pathSegments.Length == 1) { SetPropertyValue(obj, pathSegments[0], newValue); } else { // If more than one remaining segment, recurse var child = GetNamedPropertyvalue(obj, pathSegments[0]); return SetPropertyValueByPath(obj, String.Join(".", pathSegments.Skip(1)), newValue); } } 

這些方法都非常簡單。 既然你無論如何都在使用反射,你也可以全力以赴地編寫一個非泛型方法來設置任何屬性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM