我有一個深度嵌套的private欄位鏈,我想遞回地迭代它以獲取某個目標欄位的值。
如何才能做到這一點?
例如:
public class A
{
private B b;
public A(B b) { this.b = b; }
}
public class B
{
private C[] cItems;
public B(C[] cItems) { this.cItems = cItems; }
}
public class C
{
private string target; // <-- get this value
public C(int target) { this.target = val; }
}
public static void GetFieldValueByPath(object targetObj, string targetFieldPath)
{
// how to do it? I self-answer below
}
用法將是:
public void DoSomething(A a)
{
var val = GetFieldValueByPath(a, "b.cItems[2].target");
}
- 筆記:
- 有一個關于遞回獲取屬性而不是欄位的相關問題。但即便如此,它也不支持陣列欄位。
- 諸如這個用于獲取欄位的相關問題不是遞回的。
uj5u.com熱心網友回復:
該代碼適用于您的示例,但如果有字典,您可能需要更改它
public static object GetFieldValueByPath(object targetObj, string targetFieldPath)
{
var fieldNames = targetFieldPath.Split('.');
var type = targetObj.GetType();
foreach (var fieldName in fieldNames)
{
string name = fieldName;
int? objectIndex = default;
if (name.Contains('['))//getting fieldName without indexer
{
int indexerStart = name.IndexOf('[');
int indexerEnd = name.IndexOf(']');
objectIndex = int.Parse(name.Substring(indexerStart 1, indexerEnd-indexerStart - 1));
name = name.Substring(0, indexerStart);
}
var field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (objectIndex.HasValue)//here we know that field is collection
{
targetObj=((IList<object>)field.GetValue(targetObj))[0];//getting item by index
type = targetObj.GetType();
}
else
{
targetObj = field.GetValue(targetObj);
type = field.FieldType;
}
}
return targetObj;
}
uj5u.com熱心網友回復:
OfirD 的答案在正確的軌道上,但它不起作用。它不僅不能編譯,而且 C[] 也沒有實作IList<object>.
它也有很多場景沒有考慮到。(我沒有更新他的代碼來說明這些情況)
- 如果陣列沒有被整數索引怎么辦?
- 如果它是一個鋸齒狀的陣列呢?
- 如果路徑指向屬性而不是欄位怎么辦?
我已經更新了他的代碼來作業:
public static object GetFieldValueByPath(object obj, string fieldPath)
{
var flags =
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var splitted = fieldPath.Split('.');
var current = splitted[0];
int? index = null;
// Support getting a certain index in an array field
var match = Regex.Match(current, @"\[([0-9] )\]");
if (match.Groups.Count > 1)
{
current = fieldPath.Substring(0, match.Groups[0].Index);
index = int.Parse(match.Groups[1].Value);
}
var value = obj.GetType().GetField(current, flags).GetValue(obj);
if (value == null)
{
return null;
}
if (splitted.Length == 1)
{
return value;
}
if (index != null)
{
value = Index(value, index.Value);
}
return GetFieldValueByPath(value, string.Join(".", splitted.Skip(1)));
}
static object Index(object obj, int index)
{
var type = obj.GetType();
foreach (var property in obj.GetType().GetProperties())
{
var indexParams = property.GetIndexParameters();
if (indexParams.Length != 1) continue;
return property.GetValue(obj, new object[] { index });
}
throw new Exception($"{type} has no getter of the format {type}[int]");
}
uj5u.com熱心網友回復:
這是執行此操作的方法(這也支持獲取陣列欄位中的某個索引):
完整演示
- 注意:出于安全原因,此演示將來可能會失敗,原因與今天在DotNetFiddle上失敗的原因相同。
FieldAccessException
用法:
public static void Main(string[] s)
{
var a1 = new A(new B(new C[] { new C(1), new C(2), new C(3) } ) );
Console.WriteLine(GetFieldValueByPath(a1, "b.cItems[2].target"));
var a2 = new A(new B(new C[] { } ) );
Console.WriteLine(GetFieldValueByPath(a2, "b.cItems[2].target"));
var a3 = new A(new B(null) );
Console.WriteLine(GetFieldValueByPath(a3, "b.cItems[2].target"));
}
執行:
public static object GetFieldValueByPath(object obj, string fieldPath)
{
var flags =
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var splitted = fieldPath.Split('.');
var current = splitted[0];
int? index = null;
// Support getting a certain index in an array field
var match = Regex.Match(current, @"\[([0-9] )\]");
if (match.Groups.Count > 1)
{
current = fieldPath.Substring(0, match.Groups[0].Index);
index = int.Parse(match.Groups[1].Value);
}
object value;
try
{
value = obj.GetType().GetField(current, flags).GetValue(obj);
}
catch (NullReferenceException ex)
{
throw new Exception($"Wrong path provided: field '{current}' does not exist on '{obj}'");
}
if (value == null)
{
return null;
}
if (splitted.Length == 1)
{
return value;
}
if (index != null)
{
var asIList = (IList<object>)value;
if (asIList?.Count >= index.Value)
{
value = asIList[index.Value];
}
else
{
return null;
}
}
return GetFieldValueByPath(value, string.Join(".", splitted.Skip(1)));
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/328809.html
下一篇:從動態嵌套陣列生成物件的平面陣列
