c# - c# 在对象解构期间获取变量的名称
问题描述
鉴于此代码:
var (c, d) = new Test();
这可以从Deconstruct
方法中获取变量名称吗?
public class Test
{
public void Deconstruct(out string value1, out string value2)
{
// is there a way to know the parameters are
// being mapped to "c" and "d" respectively
// inside this method?
}
}
重构以下代码以减少重复量的想法:
var parsed = Regex
.Match("123: qwe: qweasd", @"(?<id>\d+?): (?<level>\d+?): (?<message>\d+?):")
.Apply(m => !m.Success ? null : new
{
// notice the names are repeated on both side
ID = m.Group["id"].Value,
Level = m.Group["level"].Value,
Message = m.Group["message"].Value,
});
我想用Test
课堂解决什么:
var (id, level, message) = Regex
.Match("123: qwe: qweasd", @"(?<id>\d+?): (?<level>\w+?): (?<message>\w+?):")
.Groups
.AsDeconstructable(); //
解决方案
我真的不认为这是一个好主意,反射可能非常慢,但我们开始吧。
首先,我们需要一些扩展来让属性和字段的处理更加简洁:
public static class HelperExtensions {
// ***
// *** Type Extensions
// ***
public static List<MemberInfo> GetPropertiesOrFields(this Type t, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
t.GetMembers(bf).Where(mi => mi.MemberType == MemberTypes.Field | mi.MemberType == MemberTypes.Property).ToList();
// ***
// *** MemberInfo Extensions
// ***
public static void SetValue<T>(this MemberInfo member, object destObject, T value) {
switch (member) {
case FieldInfo mfi:
mfi.SetValue(destObject, value);
break;
case PropertyInfo mpi:
mpi.SetValue(destObject, value);
break;
default:
throw new ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", nameof(member));
}
}
public static TOut Apply<TIn, TOut>(this TIn m, Func<TIn, TOut> applyFn) => applyFn(m);
}
然后,我们需要创建一个类来表示期望的结果:
public class ParsedMessage {
public string ID;
public string Level;
public string Message;
}
现在,我们编写一个扩展来将Group
命名值映射到对象中的属性或字段:
public static class MatchExt {
public static T MakeObjectFromGroups<T>(this Match m) where T : new() {
var members = typeof(T).GetPropertiesOrFields().ToDictionary(pf => pf.Name.ToLower());
var ans = new T();
foreach (Group g in m.Groups) {
if (members.TryGetValue(g.Name.ToLower(), out var mi))
mi.SetValue(ans, g.Value);
}
return ans;
}
public static string[] MakeArrayFromGroupValues(this Match m) {
var ans = new string[m.Groups.Count-1];
for (int j1 = 1; j1 < m.Groups.Count; ++j1)
ans[j1-1] = m.Groups[j1].Value;
return ans;
}
}
最后,我们可以使用我们的新扩展:
var parsed = Regex
.Match("123: qwe: qweasd", @"(?<id>\d+?): (?<level>\w+?): (?<message>\w+?)")
.Apply(m => m.Success ? m.MakeObjectFromGroups<ParsedMessage>() : null);
注意:可以在运行时动态创建匿名类型,但它们很少有用。由于您不知道代码中其他任何地方的属性是什么,因此您必须通过反射来完成所有操作,除非您在像 ASP.Net 这样的反射繁重的环境中使用对象,否则您最好使用 a Dictionary
,或者如果您必须,a DynamicObject
(虽然,再次,不知道不太实用的字段名称)。
我添加了一个额外的扩展来将Group
s 映射到string[]
. 由于ValueTuple
字段的名称仅在编译时可用,因此创建数组和使用索引与创建 aValueTuple
和使用Item1
等一样好。
最后,尝试使用匿名对象。通过传入匿名对象的模板,您可以从具有匹配名称的捕获组值创建一个新的匿名对象。
使用方法扩展进行类型推断:
public static class ToAnonymousExt {
public static T ToAnonymous<T>(this T patternV, Match m) {
var it = typeof(T).GetPropertiesOrFields();
var cd = m.Groups.Cast<Group>().ToDictionary(g => g.Name, g => g.Value);
return (T)Activator.CreateInstance(typeof(T), Enumerable.Range(0, it.Count).Select(n => cd[it[n].Name]).ToArray());
}
}
现在你可以传入一个匿名类型作为模板,然后得到一个填充的匿名对象。请注意,只有匿名类型中匹配捕获组名称的字段才会被填写,并且不会进行运行时错误处理。
var parsed3 = Regex.Match("123: qwe: qweasd", @"(?<id>\d+?): (?<level>\w+?): (?<message>\w+?)")
.Apply(m => m.Success ? new { message = "", id = "", level = "" }.ToAnonymous(m) : null);
推荐阅读
- javascript - Js检查对是否存在于对数组中
- csv - Prestashop 1.7 - 导入 csv(products) 的脚本,其中的列具有用逗号分隔的多个值
- python - 遍历行
- create-react-app - Teams 桌面客户端有时会缓存我的选项卡应用程序,但我无法清除它
- java - 如何在java中将单反斜杠(\)替换为双反斜杠(\\)?
- react-native - React Spring - TypeError:尝试分配给只读属性
- javascript - 正则表达式仅匹配每个单词都大写的字符串,“in”、“of”除外
- python - 提取并映射 2D numpy 数组中的列
- c - 关于后缀运算符和序列点
- python - Cython 中的布尔索引