microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit
如何应聘高薪职位
FieldLoader 类代码详细解读
代码注释与逻辑解析
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
// FieldLoader 静态类:负责将 YAML 数据加载到对象字段中
public static class FieldLoader
{
// 用于分割逗号分隔的值
const char SplitComma = ',';
// 表示缺少必需字段的异常
public class MissingFieldsException : YamlException
{
// 缺少的字段名称数组
public readonly string[] Missing;
// 异常标题
public readonly string Header;
// 重写 Message 属性,用于格式化异常消息
public override string Message
{
get
{
return (string.IsNullOrEmpty(Header) ? "" : Header + ": ") + Missing[0]
+ string.Concat(Missing.Skip(1).Select(m => ", " + m));
}
}
// 构造函数:接收缺少的字段列表和标题
public MissingFieldsException(string[] missing, string header = null, string headerSingle = null)
: base(null)
{
// 根据缺少字段数量决定使用哪个标题
Header = missing.Length > 1 ? header : headerSingle ?? header;
Missing = missing;
}
}
// 处理无效值的委托函数,默认是抛出异常
public static Func<string, Type, string, object> InvalidValueAction = (s, t, f) =>
throw new YamlException($"FieldLoader: Cannot parse `{s}` into `{f}.{t}` ");
// 处理未知字段的委托函数,默认是抛出异常
public static Action<string, Type> UnknownFieldAction = (s, f) =>
throw new NotImplementedException($"FieldLoader: Missing field `{s}` on `{f.Name}`");
// 类型加载信息的并发缓存,避免重复构建
static readonly ConcurrentCache<Type, FieldLoadInfo[]> TypeLoadInfo =
new(BuildTypeLoadInfo);
// 布尔表达式的并发缓存
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
new(expression => new BooleanExpression(expression));
// 整数表达式的并发缓存
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
new(expression => new IntegerExpression(expression));
// 基本类型解析器字典,将字符串值转换为特定类型的对象
static readonly Dictionary<Type, Func<string, Type, string, MemberInfo, object>> TypeParsers =
new()
{
// 整数类型解析
{ typeof(int), ParseInt },
{ typeof(ushort), ParseUShort },
{ typeof(long), ParseLong },
// 浮点数类型解析
{ typeof(float), ParseFloat },
{ typeof(decimal), ParseDecimal },
// 字符串解析(直接返回)
{ typeof(string), ParseString },
// 颜色解析
{ typeof(Color), ParseColor },
// 热键相关解析
{ typeof(Hotkey), ParseHotkey },
{ typeof(HotkeyReference), ParseHotkeyReference },
// 游戏中的位置和距离单位解析
{ typeof(WDist), ParseWDist },
{ typeof(WVec), ParseWVec },
{ typeof(WVec[]), ParseWVecArray },
{ typeof(WPos), ParseWPos },
{ typeof(WAngle), ParseWAngle },
{ typeof(WRot), ParseWRot },
{ typeof(CPos), ParseCPos },
{ typeof(CPos[]), ParseCPosArray },
{ typeof(CVec), ParseCVec },
{ typeof(CVec[]), ParseCVecArray },
// 表达式解析
{ typeof(BooleanExpression), ParseBooleanExpression },
{ typeof(IntegerExpression), ParseIntegerExpression },
// 枚举解析
{ typeof(Enum), ParseEnum },
// 布尔值解析
{ typeof(bool), ParseBool },
// 向量和矩形解析
{ typeof(int2[]), ParseInt2Array },
{ typeof(Size), ParseSize },
{ typeof(int2), ParseInt2 },
{ typeof(float2), ParseFloat2 },
{ typeof(float3), ParseFloat3 },
{ typeof(Rectangle), ParseRectangle },
// 日期时间解析
{ typeof(DateTime), ParseDateTime }
};
// 泛型类型解析器字典
static readonly Dictionary<Type, Func<string, Type, string, MiniYaml, MemberInfo, object>> GenericTypeParsers =
new()
{
// 集合和列表解析
{ typeof(HashSet<>), ParseHashSetOrList },
{ typeof(List<>), ParseHashSetOrList },
// 字典解析
{ typeof(Dictionary<,>), ParseDictionary },
// 位集合解析
{ typeof(BitSet<>), ParseBitSet },
// 可空类型解析
{ typeof(Nullable<>), ParseNullable },
};
// 预先装箱的布尔值,避免频繁装箱操作
static readonly object BoxedTrue = true;
static readonly object BoxedFalse = false;
// 预先装箱的整数 0-32,避免频繁装箱操作
static readonly object[] BoxedInts = Exts.MakeArray(33, i => (object)i);
// 解析整数
static object ParseInt(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (Exts.TryParseInt32Invariant(value, out var res))
{
// 使用预先装箱的整数,提高性能
if (res >= 0 && res < BoxedInts.Length)
return BoxedInts[res];
return res;
}
// 无法解析,调用无效值处理函数
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析无符号短整数
static object ParseUShort(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析长整数
static object ParseLong(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析浮点数,支持百分比表示
static object ParseFloat(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
return res * (value.Contains('%') ? 0.01f : 1f); // 如果包含%,则乘以0.01转换为小数
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析decimal,支持百分比表示
static object ParseDecimal(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
return res * (value.Contains('%') ? 0.01m : 1m); // 如果包含%,则乘以0.01转换为小数
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析字符串(直接返回)
static object ParseString(string fieldName, Type fieldType, string value, MemberInfo field)
{
return value;
}
// 解析颜色
static object ParseColor(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null && Color.TryParse(value, out var color))
return color;
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析热键
static object ParseHotkey(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (Hotkey.TryParse(value, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析热键引用
static object ParseHotkeyReference(string fieldName, Type fieldType, string value, MemberInfo field)
{
return Game.ModData.Hotkeys[value];
}
// 解析世界距离单位WDist
static object ParseWDist(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (WDist.TryParse(value, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析世界向量WVec
static object ParseWVec(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma);
if (parts.Length == 3
&& WDist.TryParse(parts[0], out var rx)
&& WDist.TryParse(parts[1], out var ry)
&& WDist.TryParse(parts[2], out var rz))
return new WVec(rx, ry, rz);
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析世界向量数组WVec[]
static object ParseWVecArray(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma);
if (parts.Length % 3 != 0)
return InvalidValueAction(value, fieldType, fieldName);
var vecs = new WVec[parts.Length / 3];
for (var i = 0; i < vecs.Length; ++i)
{
if (WDist.TryParse(parts[3 * i], out var rx)
&& WDist.TryParse(parts[3 * i + 1], out var ry)
&& WDist.TryParse(parts[3 * i + 2], out var rz))
vecs[i] = new WVec(rx, ry, rz);
}
return vecs;
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析世界位置WPos
static object ParseWPos(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma);
if (parts.Length == 3
&& WDist.TryParse(parts[0], out var rx)
&& WDist.TryParse(parts[1], out var ry)
&& WDist.TryParse(parts[2], out var rz))
return new WPos(rx, ry, rz);
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析世界角度WAngle
static object ParseWAngle(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (Exts.TryParseInt32Invariant(value, out var res))
return new WAngle(res);
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析世界旋转WRot
static object ParseWRot(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma);
if (parts.Length == 3
&& Exts.TryParseInt32Invariant(parts[0], out var rr)
&& Exts.TryParseInt32Invariant(parts[1], out var rp)
&& Exts.TryParseInt32Invariant(parts[2], out var ry))
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析单元格位置CPos
static object ParseCPos(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3)
return new CPos(
Exts.ParseInt32Invariant(parts[0]),
Exts.ParseInt32Invariant(parts[1]),
Exts.ParseByteInvariant(parts[2]));
return new CPos(Exts.ParseInt32Invariant(parts[0]), Exts.ParseInt32Invariant(parts[1]));
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析单元格位置数组CPos[]
static object ParseCPosArray(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma);
if (parts.Length % 2 != 0)
return InvalidValueAction(value, fieldType, fieldName);
var vecs = new CPos[parts.Length / 2];
for (var i = 0; i < vecs.Length; i++)
{
if (int.TryParse(parts[2 * i], out var rx)
&& int.TryParse(parts[2 * i + 1], out var ry))
vecs[i] = new CPos(rx, ry);
}
return vecs;
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析单元格向量CVec
static object ParseCVec(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
return new CVec(Exts.ParseInt32Invariant(parts[0]), Exts.ParseInt32Invariant(parts[1]));
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析单元格向量数组CVec[]
static object ParseCVecArray(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma);
if (parts.Length % 2 != 0)
return InvalidValueAction(value, fieldType, fieldName);
var vecs = new CVec[parts.Length / 2];
for (var i = 0; i < vecs.Length; i++)
{
if (int.TryParse(parts[2 * i], out var rx)
&& int.TryParse(parts[2 * i + 1], out var ry))
vecs[i] = new CVec(rx, ry);
}
return vecs;
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析布尔表达式
static object ParseBooleanExpression(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
try
{
// 尝试从缓存获取或创建新的布尔表达式
return BooleanExpressionCache[value];
}
catch (InvalidDataException e)
{
throw new YamlException(e.Message);
}
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析整数表达式
static object ParseIntegerExpression(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
try
{
// 尝试从缓存获取或创建新的整数表达式
return IntegerExpressionCache[value];
}
catch (InvalidDataException e)
{
throw new YamlException(e.Message);
}
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析枚举
static object ParseEnum(string fieldName, Type fieldType, string value, MemberInfo field)
{
try
{
return Enum.Parse(fieldType, value, true);
}
catch (ArgumentException)
{
return InvalidValueAction(value, fieldType, fieldName);
}
}
// 解析布尔值
static object ParseBool(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (bool.TryParse(value.ToLowerInvariant(), out var result))
return result ? BoxedTrue : BoxedFalse; // 使用预先装箱的布尔值
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析int2数组
static object ParseInt2Array(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length % 2 != 0)
return InvalidValueAction(value, fieldType, fieldName);
var ints = new int2[parts.Length / 2];
for (var i = 0; i < ints.Length; i++)
ints[i] = new int2(Exts.ParseInt32Invariant(parts[2 * i]), Exts.ParseInt32Invariant(parts[2 * i + 1]));
return ints;
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析Size
static object ParseSize(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
return new Size(Exts.ParseInt32Invariant(parts[0]), Exts.ParseInt32Invariant(parts[1]));
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析int2
static object ParseInt2(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
return InvalidValueAction(value, fieldType, fieldName);
return new int2(Exts.ParseInt32Invariant(parts[0]), Exts.ParseInt32Invariant(parts[1]));
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析float2,支持百分比
static object ParseFloat2(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
float xx = 0;
float yy = 0;
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
xx = res * (parts[0].Contains('%') ? 0.01f : 1f);
if (float.TryParse(parts[1].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
yy = res * (parts[1].Contains('%') ? 0.01f : 1f);
return new float2(xx, yy);
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析float3
static object ParseFloat3(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var x);
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var y);
// z 组件是可选的,兼容旧的 float2 定义
float z = 0;
if (parts.Length > 2)
float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z);
return new float3(x, y, z);
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析矩形
static object ParseRectangle(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
return new Rectangle(
Exts.ParseInt32Invariant(parts[0]),
Exts.ParseInt32Invariant(parts[1]),
Exts.ParseInt32Invariant(parts[2]),
Exts.ParseInt32Invariant(parts[3]));
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析日期时间
static object ParseDateTime(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dt))
return dt;
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析HashSet或List集合
static object ParseHashSetOrList(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
if (value == null)
return Activator.CreateInstance(fieldType);
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
var set = Activator.CreateInstance(fieldType, parts.Length);
var arguments = fieldType.GetGenericArguments();
var addMethod = fieldType.GetMethod(nameof(List<object>.Add), arguments);
var addArgs = new object[1];
for (var i = 0; i < parts.Length; i++)
{
addArgs[0] = GetValue(fieldName, arguments[0], parts[i].Trim(), field);
addMethod.Invoke(set, addArgs);
}
return set;
}
// 解析字典
static object ParseDictionary(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
if (yaml == null)
return Activator.CreateInstance(fieldType);
var dict = Activator.CreateInstance(fieldType, yaml.Nodes.Length);
var arguments = fieldType.GetGenericArguments();
var addMethod = fieldType.GetMethod(nameof(Dictionary<object, object>.Add), arguments);
var addArgs = new object[2];
foreach (var node in yaml.Nodes)
{
addArgs[0] = GetValue(fieldName, arguments[0], node.Key, field);
addArgs[1] = GetValue(fieldName, arguments[1], node.Value, field);
addMethod.Invoke(dict, addArgs);
}
return dict;
}
// 解析位集合
static object ParseBitSet(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
if (value != null)
{
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
var ctor = fieldType.GetConstructor(new[] { typeof(string[]) });
return ctor.Invoke(new object[] { parts.Select(p => p.Trim()).ToArray() });
}
return InvalidValueAction(value, fieldType, fieldName);
}
// 解析可空类型
static object ParseNullable(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
if (string.IsNullOrEmpty(value))
return null;
var innerType = fieldType.GetGenericArguments()[0];
var innerValue = GetValue("Nullable<T>", innerType, value, field);
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
}
// 从MiniYaml加载数据到对象
public static void Load(object self, MiniYaml my)
{
var loadInfo = TypeLoadInfo[self.GetType()];
List<string> missing = null;
Dictionary<string, MiniYaml> md = null;
foreach (var fli in loadInfo)
{
object val;
// 延迟初始化字典
md ??= my.ToDictionary();
// 使用自定义加载器
if (fli.Loader != null)
{
if (!fli.Attribute.Required || md.ContainsKey(fli.YamlName))
val = fli.Loader(my);
else
{
// 记录缺少的必需字段
missing ??= new List<string>();
missing.Add(fli.YamlName);
continue;
}
}
else
{
// 尝试从YAML获取值
if (!TryGetValueFromYaml(fli.YamlName, fli.Field, md, out val))
{
if (fli.Attribute.Required)
{
// 记录缺少的必需字段
missing ??= new List<string>();
missing.Add(fli.YamlName);
}
continue;
}
}
// 设置对象的字段值
fli.Field.SetValue(self, val);
}
// 如果有缺少的必需字段,抛出异常
if (missing != null)
throw new MissingFieldsException(missing.ToArray());
}
// 尝试从YAML字典获取值
static bool TryGetValueFromYaml(string yamlName, FieldInfo field, Dictionary<string, MiniYaml> md, out object ret)
{
ret = null;
if (!md.TryGetValue(yamlName, out var yaml))
return false;
ret = GetValue(field.Name, field.FieldType, yaml, field);
return true;
}
// 泛型加载方法
public static T Load<T>(MiniYaml y) where T : new()
{
var t = new T();
Load(t, y);
return t;
}
// 加载单个字段
public static void LoadField(object target, string key, string value)
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
key = key.Trim();
// 尝试找到字段
var field = target.GetType().GetField(key, Flags);
if (field != null)
{
var sa = field.GetCustomAttributes<SerializeAttribute>(false).DefaultIfEmpty(SerializeAttribute.Default).First();
if (!sa.FromYamlKey)
field.SetValue(target, GetValue(field.Name, field.FieldType, value, field));
return;
}
// 尝试找到属性
var prop = target.GetType().GetProperty(key, Flags);
if (prop != null)
{
var sa = prop.GetCustomAttributes<SerializeAttribute>(false).DefaultIfEmpty(SerializeAttribute.Default).First();
if (!sa.FromYamlKey)
prop.SetValue(target, GetValue(prop.Name, prop.PropertyType, value, prop), null);
return;
}
// 找不到字段或属性
UnknownFieldAction(key, target.GetType());
}
// 获取类型化的值
public static T GetValue<T>(string field, string value)
{
return (T)GetValue(field, typeof(T), value, null);
}
// 获取值的重载方法
public static object GetValue(string fieldName, Type fieldType, string value)
{
return GetValue(fieldName, fieldType, value, null);
}
// 获取值的重载方法,包含字段信息
public static object GetValue(string fieldName, Type fieldType, string value, MemberInfo field)
{
return GetValue(fieldName, fieldType, value, null, field);
}
// 从MiniYaml获取值
public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field)
{
return GetValue(fieldName, fieldType, yaml.Value, yaml, field);
}
// 获取值的核心方法
static object GetValue(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
value = value?.Trim();
// 处理泛型类型
if (fieldType.IsGenericType)
{
if (GenericTypeParsers.TryGetValue(fieldType.GetGenericTypeDefinition(), out var parseFuncGeneric))
return parseFuncGeneric(fieldName, fieldType, value, yaml, field);
}
else
{
// 处理非泛型类型
if (TypeParsers.TryGetValue(fieldType, out var parseFunc))
return parseFunc(fieldName, fieldType, value, field);
// 处理一维数组
if (fieldType.IsArray && fieldType.GetArrayRank() == 1)
{
if (value == null)
return Array.CreateInstance(fieldType.GetElementType(), 0);
var options = field != null && field.HasAttribute<AllowEmptyEntriesAttribute>() ?
StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries;
var parts = value.Split(SplitComma, options);
var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length);
for (var i = 0; i < parts.Length; i++)
ret.SetValue(GetValue(fieldName, fieldType.GetElementType(), parts[i].Trim(), field), i);
return ret;
}
}
// 尝试使用类型转换器
var conv = TypeDescriptor.GetConverter(fieldType);
if (conv.CanConvertFrom(typeof(string)))
{
try
{
return conv.ConvertFromInvariantString(value);
}
catch
{
return InvalidValueAction(value, fieldType, fieldName);
}
}
// 未知类型
UnknownFieldAction($"[Type] {value}", fieldType);
return null;
}
// 字段加载信息类
public sealed class FieldLoadInfo
{
// 字段信息
public readonly FieldInfo Field;
// 序列化特性
public readonly SerializeAttribute Attribute;
// YAML中的字段名
public readonly string YamlName;
// 自定义加载器
public readonly Func<MiniYaml, object> Loader;
// 构造函数
internal FieldLoadInfo(FieldInfo field, SerializeAttribute attr, string yamlName, Func<MiniYaml, object> loader = null)
{
Field = field;
Attribute = attr;
YamlName = yamlName;
Loader = loader;
}
}
// 获取类型的加载信息
public static IEnumerable<FieldLoadInfo> GetTypeLoadInfo(Type type, bool includePrivateByDefault = false)
{
return TypeLoadInfo[type].Where(fli => includePrivateByDefault || fli.Field.IsPublic || (fli.Attribute.Serialize && !fli.Attribute.IsDefault));
}
// 构建类型加载信息
static FieldLoadInfo[] BuildTypeLoadInfo(Type type)
{
var ret = new List<FieldLoadInfo>();
// 遍历类型的所有字段
foreach (var ff in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var field = ff;
// 获取序列化特性
var sa = field.GetCustomAttributes<SerializeAttribute>(false).DefaultIfEmpty(SerializeAttribute.Default).First();
if (!sa.Serialize)
continue;
// 确定YAML中的字段名
var yamlName = string.IsNullOrEmpty(sa.YamlName) ? field.Name : sa.YamlName;
// 获取自定义加载器
var loader = sa.GetLoader(type);
if (loader == null && sa.FromYamlKey)
loader = yaml => GetValue(yamlName, field.FieldType, yaml, field);
// 创建并添加字段加载信息
var fli = new FieldLoadInfo(field, sa, yamlName, loader);
ret.Add(fli);
}
return ret.ToArray();
}
// 忽略字段特性
[AttributeUsage(AttributeTargets.Field)]
public sealed class IgnoreAttribute : SerializeAttribute
{
public IgnoreAttribute()
: base(false) { }
}
// 必需字段特性
[AttributeUsage(AttributeTargets.Field)]
public sealed class RequireAttribute : SerializeAttribute
{
public RequireAttribute()
: base(true, true) { }
}
// 允许空条目特性
[AttributeUsage(AttributeTargets.Field)]
public sealed class AllowEmptyEntriesAttribute : SerializeAttribute
{
public AllowEmptyEntriesAttribute()
: base(allowEmptyEntries: true) { }
}
// 使用自定义加载器特性
[AttributeUsage(AttributeTargets.Field)]
public sealed class LoadUsingAttribute : SerializeAttribute
{
public LoadUsingAttribute(string loader, bool required = false)
{
Loader = loader;
Required = required;
}
}
// 序列化特性基类
[AttributeUsage(AttributeTargets.Field)]
public class SerializeAttribute : Attribute
{
// 默认实例
public static readonly SerializeAttribute Default = new(true);
// 是否为默认实例
public bool IsDefault => this == Default;
// 是否序列化
public readonly bool Serialize;
// YAML中的字段名
public string YamlName;
// 加载器方法名
public string Loader;
// 是否从YAML键加载
public bool FromYamlKey;
// 是否字典从YAML键加载
public bool DictionaryFromYamlKey;
// 是否必需
public bool Required;
// 是否允许空条目
public bool AllowEmptyEntries;
// 构造函数
public SerializeAttribute(bool serialize = true, bool required = false, bool allowEmptyEntries = false)
{
Serialize = serialize;
Required = required;
AllowEmptyEntries = allowEmptyEntries;
}
// 获取加载器委托
internal Func<MiniYaml, object> GetLoader(Type type)
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy;
if (!string.IsNullOrEmpty(Loader))
{
var method = type.GetMethod(Loader, Flags);
if (method == null)
throw new InvalidOperationException($"{type.Name} does not specify a loader function '{Loader}'");
return (Func<MiniYaml, object>)Delegate.CreateDelegate(typeof(Func<MiniYaml, object>), method);
}
return null;
}
}
}
// 从YAML键加载字段特性
[AttributeUsage(AttributeTargets.Field)]
public sealed class FieldFromYamlKeyAttribute : FieldLoader.SerializeAttribute
{
public FieldFromYamlKeyAttribute()
{
FromYamlKey = true;
}
}
// 从YAML键加载字典特性
[AttributeUsage(AttributeTargets.Field)]
public sealed class DictionaryFromYamlKeyAttribute : FieldLoader.SerializeAttribute
{
public DictionaryFromYamlKeyAttribute()
{
FromYamlKey = true;
DictionaryFromYamlKey = true;
}
}
// 描述特性
[AttributeUsage(AttributeTargets.All)]
public sealed class DescAttribute : Attribute
{
public readonly string[] Lines;
public DescAttribute(params string[] lines) { Lines = lines; }
}
}
逻辑解读
FieldLoader
是 OpenRA 游戏引擎中的一个核心工具类,用于从 YAML 配置文件中加载数据到 C# 对象。它的主要职责是处理各种类型的值的反序列化,从字符串转换为强类型对象。以下是其核心逻辑:
- 类型解析系统:维护了一套完整的类型解析器,用于将字符串值转换为各种类型的对象,包括基本类型和游戏特定类型。
- 缓存机制:使用并发缓存存储类型加载信息和表达式,避免重复计算,提高性能。
- 反射机制:使用反射获取类型的字段信息,并根据字段的类型和特性决定如何加载值。
- 特性系统:通过自定义特性(Attributes)控制序列化行为,如是否序列化、是否必需、自定义加载器等。
- 错误处理:提供详细的错误信息和异常处理机制,帮助开发者识别配置错误。
流程图
特殊语法与技巧解读
-
静态类与静态成员
public static class FieldLoader
解读:定义一个只包含静态成员的工具类,无需实例化即可使用,适合于提供全局功能的工具类。
-
泛型缓存
static readonly ConcurrentCache<Type, FieldLoadInfo[]> TypeLoadInfo = new(BuildTypeLoadInfo);
解读:使用泛型并发缓存,避免重复构建相同类型的加载信息,提高性能。缓存接受一个工厂函数,当缓存未命中时调用。
-
函数类型和委托
public static Func<string, Type, string, object> InvalidValueAction = (s, t, f) => throw new YamlException($"FieldLoader: Cannot parse `{s}` into `{f}.{t}` ");
解读:使用
Func<>
和Action<>
声明委托类型的变量,并使用 lambda 表达式初始化,允许定制化错误处理行为。 -
字典初始化器
static readonly Dictionary<Type, Func<string, Type, string, MemberInfo, object>> TypeParsers = new() { { typeof(int), ParseInt }, // ...其他解析器 };
解读:使用集合初始化器语法简洁地初始化字典,提高代码可读性。C# 9.0 中可以省略
Dictionary<,>()
后的类型参数。 -
预装箱值
static readonly object BoxedTrue = true; static readonly object BoxedFalse = false; static readonly object[] BoxedInts = Exts.MakeArray(33, i => (object)i);
解读:预先将值类型装箱为引用类型,避免频繁装箱操作,提高性能。特别是对于频繁使用的小值范围的整数和布尔值。
-
空条件运算符
md ??= my.ToDictionary();
解读:C# 8.0 引入的空合并赋值运算符,当左侧为 null 时,将右侧的值赋给左侧,简化了空检查和赋值操作。
-
字符串内插
throw new YamlException($"FieldLoader: Cannot parse `{s}` into `{f}.{t}` ");
解读:使用
$
前缀的字符串内插语法,将变量值直接嵌入到字符串中,比传统的字符串拼接更简洁可读。 -
特性(Attributes)系统
[AttributeUsage(AttributeTargets.Field)] public sealed class RequireAttribute : SerializeAttribute { public RequireAttribute() : base(true, true) { } }
解读:使用特性为字段添加元数据,通过反射在运行时获取和使用这些元数据,实现声明式编程。特性可以继承,形成层次结构。
-
反射机制
var field = target.GetType().GetField(key, Flags); addMethod.Invoke(set, addArgs);
解读:使用反射动态获取类型的字段和方法,并通过
Invoke
调用方法,实现运行时的动态操作。 -
泛型约束
public static T Load<T>(MiniYaml y) where T : new()
解读:使用泛型约束
where T : new()
,限制类型参数必须有无参构造函数,使得可以在方法内创建泛型类型的实例。 -
扩展方法
// 在其他地方定义的扩展方法 md.ToDictionary(); field.HasAttribute<AllowEmptyEntriesAttribute>();
解读:使用扩展方法为现有类型添加新功能,而不需要修改原始类型的定义,增强代码的可扩展性。
-
LINQ 查询
return TypeLoadInfo[type].Where(fli => includePrivateByDefault || fli.Field.IsPublic || (fli.Attribute.Serialize && !fli.Attribute.IsDefault));
解读:使用 LINQ 进行集合查询和过滤,简化集合操作代码,提高可读性。
-
可选参数
public SerializeAttribute(bool serialize = true, bool required = false, bool allowEmptyEntries = false)
解读:使用默认参数值,简化方法调用,在不需要指定特定参数值时可以使用默认值。
-
自定义异常
public class MissingFieldsException : YamlException
解读:通过继承基础异常类创建特定于应用程序域的自定义异常,提供更详细的错误信息和处理机制。
-
条件编译
const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
解读:使用位标志枚举,通过位运算组合多个选项,提高代码灵活性和可配置性。
-
特性继承
public sealed class IgnoreAttribute : SerializeAttribute { public IgnoreAttribute() : base(false) { } }
解读:通过继承基类特性创建具有特定行为的特化特性,简化特性的使用,提高代码可读性。
-
延迟求值
missing ??= new List<string>();
解读:只在需要时才创建对象,减少内存使用,提高性能。
-
泛型和反射结合
var arguments = fieldType.GetGenericArguments(); var addMethod = fieldType.GetMethod(nameof(List<object>.Add), arguments);
解读:结合使用泛型和反射,动态调用泛型方法,增强代码的灵活性。
-
委托创建
return (Func<MiniYaml, object>)Delegate.CreateDelegate(typeof(Func<MiniYaml, object>), method);
解读:使用反射动态创建委托,将方法引用转换为强类型委托,实现运行时动态调用。
-
字符串处理
value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries)
解读:使用
StringSplitOptions
控制字符串分割行为,忽略空条目,提高字符串处理的灵活性。
这些语法和技巧展示了 C# 语言的强大功能和灵活性,使得 OpenRA 游戏引擎能够高效、灵活地处理游戏配置数据。
从ruby不适开发游戏说起
3D人物说话时的嘴部动作与表情与语音如何配合到一起的
车载系统软件工程师如何实现车载系统的紧急制动和碰撞预警
python进行因子分析(Factor Analysis,简称FA)
车载系统软件工程师如何实现车载系统的音频处理和优化
C#进行串口应用开发如何将串口数据保存到文件
c#视觉应用开发中如何在C#中进行图像去雪?
量化交易系统中如何处理测试中的数据偏差和异常?
microPython的源码解析之 mpprint.c
linux其实比windows更适合程序开发
车载系统软件工程师如何实现车载系统的驾驶习惯学习和优化
量化交易策略 行业板块选择
python 的pickle库如何使用
c#程序与USB HID设备进行通信
OpenALPR库如何使用
工业运动控制涉及到哪些设备和技术
python的injectool库
量子计算Grover搜索算法
量化交易系统中+如何管理策略的生命周期(从开发到部署)?
量化交易系统中+如何使用实时分析工具(如Kafka、Flink)?
C#进行串口应用开发如何实现串口通信的校验与数据校正
python的任务调度库 Advanced Python Scheduler (APScheduler)
microPython的源码解析之 modbuiltins.c
运动控制卡
车载系统软件工程师如何实现车载导航和GPS系统
jupyter深度理解三 之nbformat
车载系统软件工程师如何实现车载系统的紧急呼叫服务(eCall)
NI-Motion如何在双轴舞台上进行轮廓扫描的过程的C语言示例代码
python如何判断一个文件是否已经写入完成
python使用原始套接字的ICMP ping实现库AsyncPing
c++加QT开发linux远程终端,类似putty
如何给一个客户端分配多个IP
量化交易系统中+如何应用机器学习进行预测和决策?
一家初创医疗科技公司用Python设计了一个平台
c#视觉应用开发中如何在C#中进行图像立体匹配?
我的创作纪念日
Python如何把一个列表按照一定数量均匀的切片
量化交易系统中+如何分析和优化交易成本?
车载系统软件工程师如何实现车载系统的远程控制和监控
c++,qt 如何动态获取类的字段的名称和数据
Union Investment如何利用Python和机器学习(ML)技术来改进其投资流程
智能农业设备软件工程师如何实现农业设备的用户权限管理
智能农业设备软件工程师如何实现农业设备的人工智能和机器学习应用
智能农业设备软件工程师如何实现农业设备的精准农业应用
python如何自动创建python代码
python的smtplib
简单解释量子计算
python的pytables库如何使用
microPython的源码解析之 objstrunicode.c
车载系统软件工程师如何处理车载系统的系统升级和版本控制
开源的全文搜索引擎Elasticsearch
openAI,fine-tuning的示例代码
microPython的源码解析之 asmarm.c
python web应用开发神器 入门二十四
在进行股票统计研究中,有很多因子,如何屏蔽其他因子的影响,只研究一个因子的影响大小呢
c#视觉应用开发中如何在C#中进行图像数据库管理?
智能农业设备软件工程师如何实现农业设备的驾驶员辅助功能
Deepmind开发了哪些开源的AI项目
c#视觉应用开发中如何在C#中进行图像去干扰?
计算图是什么,如何理解
python pyqt 开发一个linux远程终端工具
microPython的源码解析之 objzip.c
c#视觉应用开发中如何在C#中进行图像去伪影?
C#进行串口应用开发如何优化串口通信的实时性与吞吐量
车载系统软件工程师如何实现车载系统的蓝牙和无线连接
量化交易系统中+如何选择和实现合适的算法和模型?
量化交易系统中+如何设计高效的数据库架构?
Python的pyi文件的作用.
C#进行串口应用开发如何捕获和分析串口通信的数据包
车载系统软件工程师如何实现车载系统的实时交通管理
C#进行串口应用开发如何实现串口通信的统计与性能分析
c# 如何操作usb设备
python如何操作word文档
C#进行串口应用开发如何处理Windows下修改串口参数后导致的系统死机问题
量化交易系统中+如何设计和实现系统的监控和报警机制?
Q#量子计算示例代码
microPython的源码解析之 scope.c
linux的命令体系有什么优势
openai的的API如何使用
如何用c#语言进行开发一个edge浏览器插件
量化交易策略 随机游走
c#视觉应用开发中如何在C#中进行图像增强?
车载系统软件工程师如何实现车载系统的驾驶员监控系统
python的数据降维库umap
c#视觉应用开发中如何在C#中进行图像去雨?
Python通过写一个射箭小游戏来详细展示物理引擎的使用
python如何实现更精确的定时任务
量化对冲交易系统设计一
python的pure-eval库
python web应用开发神器 入门五
SSH服务以及pxssh的使用
c#视觉应用开发中如何使用Emgu CV在C#中进行图像处理?
C#进行串口应用开发如何实现串口通信双机热备份和网络Tolerant
Linux 的shell的 bomb函数
python的Scapy解析TTL字段的值
microPython的源码解析之 smallint.c
python如何显示html文档
C#进行串口应用开发如何实现串口数据的校验
智能农业设备软件工程师如何处理设备的环境适应性
python如何使用Windows API 和 COM 接口