轻量级元编程场景
使用特性驱动的辅助工具:显示标签、必填验证、小型构造函数激活器,以及带有明确安全提示的类 CSV 序列化。
轻量级元编程场景 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 3 节课。
计划与范围
目标:使用特性驱动小型行为。
- 映射标签
- 验证必填字段
- 迷你 DI 激活器
- 序列化为类似 CSV 的文本
特性标签
使用 DisplayName 修饰属性,并通过反射读取它,以打印易于理解的标签。
using System;
using System.Reflection;
[AttributeUsage(AttributeTargets.Property)]
public sealed class DisplayNameAttribute : Attribute
{
public string Text { get; private set; }
public DisplayNameAttribute(string text) { Text = text; }
}
public sealed class User
{
[DisplayName("User Id")]
public int Id { get; set; }
[DisplayName("Full Name")]
public string Name { get; set; }
}
public class Program
{
static void PrintWithLabels(object o)
{
if (o == null) throw new ArgumentNullException("o");
Type t = o.GetType();
foreach (PropertyInfo p in t.GetProperties())
{
object[] at = p.GetCustomAttributes(typeof(DisplayNameAttribute), false);
string label = at.Length > 0 ? ((DisplayNameAttribute)at[0]).Text : p.Name;
object val = p.GetValue(o, null);
Console.WriteLine(label + ": " + (val == null ? "null" : val.ToString()));
}
}
public static void Main(string[] args)
{
User u = new User { Id = 1, Name = "Ada" };
PrintWithLabels(u);
}
}
特性验证
使用 Required 标记属性;小型验证器会检查属性,并报告第一个缺失的字段。
using System;
using System.Reflection;
[AttributeUsage(AttributeTargets.Property)]
public sealed class RequiredAttribute : Attribute { }
public sealed class Product
{
[Required] public string Name { get; set; }
public decimal Price { get; set; }
}
public class Program
{
static bool Validate(object obj, out string error)
{
error = null;
if (obj == null) { error = "object is null"; return false; }
Type t = obj.GetType();
foreach (PropertyInfo p in t.GetProperties())
{
object[] req = p.GetCustomAttributes(typeof(RequiredAttribute), false);
if (req.Length > 0)
{
object v = p.GetValue(obj, null);
if (v == null || (v is string && string.IsNullOrEmpty((string)v)))
{
error = "Required property missing: " + p.Name;
return false;
}
}
}
return true;
}
public static void Main(string[] args)
{
Product ok = new Product { Name = "Mouse", Price = 10m };
Product bad = new Product { Name = null, Price = 5m };
string e;
Console.WriteLine(Validate(ok, out e) ? "OK" : e);
Console.WriteLine(Validate(bad, out e) ? "OK" : e);
}
}
迷你激活器
小型激活器会将名称映射到每个构造函数参数;缺少值时提供默认值。
using System;
using System.Reflection;
using System.Collections.Generic;
public sealed class Repo { public string Name; public Repo(string name){ Name = name; } }
public sealed class Service
{
public Repo R; public int Timeout;
public Service(Repo repo, int timeout){ R = repo; Timeout = timeout; }
}
public class Program
{
static object CreateWithArgs(Type t, IDictionary<string, object> args)
{
if (t == null) throw new ArgumentNullException("t");
if (args == null) args = new Dictionary<string, object>();
ConstructorInfo[] ctors = t.GetConstructors();
if (ctors.Length == 0) throw new InvalidOperationException("No public constructor");
ConstructorInfo ci = ctors[0];
ParameterInfo[] ps = ci.GetParameters();
object[] argv = new object[ps.Length];
for (int i = 0; i < ps.Length; i++)
{
object val;
if (args.TryGetValue(ps[i].Name, out val)) argv[i] = val;
else if (ps[i].ParameterType.IsValueType) argv[i] = Activator.CreateInstance(ps[i].ParameterType);
else argv[i] = null;
}
return ci.Invoke(argv);
}
public static void Main(string[] args)
{
IDictionary<string, object> map = new Dictionary<string, object>();
map["repo"] = new Repo("Main");
map["timeout"] = 30;
Service s = (Service)CreateWithArgs(typeof(Service), map);
Console.WriteLine(s.R.Name + " / " + s.Timeout);
}
}
通过特性处理 CSV
使用 CsvOrder 排列列,并正确转义逗号和引号。
using System;
using System.Reflection;
using System.Text;
using System.Globalization;
[AttributeUsage(AttributeTargets.Property)]
public sealed class CsvOrderAttribute : Attribute
{
public int Index { get; private set; }
public CsvOrderAttribute(int index){ Index = index; }
}
public sealed class Row
{
[CsvOrder(0)] public int Id { get; set; }
[CsvOrder(1)] public string Name { get; set; }
[CsvOrder(2)] public decimal Price { get; set; }
}
public class Program
{
static int GetIndex(PropertyInfo p)
{
object[] at = p.GetCustomAttributes(typeof(CsvOrderAttribute), false);
return at.Length > 0 ? ((CsvOrderAttribute)at[0]).Index : Int32.MaxValue;
}
static string ToCsv(object o)
{
if (o == null) throw new ArgumentNullException("o");
Type t = o.GetType();
PropertyInfo[] props = t.GetProperties();
Array.Sort(props, delegate(PropertyInfo a, PropertyInfo b)
{
int ia = GetIndex(a), ib = GetIndex(b);
return ia.CompareTo(ib);
});
StringBuilder sb = new StringBuilder();
for (int i = 0; i < props.Length; i++)
{
object v = props[i].GetValue(o, null);
string cell = v == null ? "" : Convert.ToString(v, CultureInfo.InvariantCulture);
if (i > 0) sb.Append(",");
sb.Append(cell);
}
return sb.ToString();
}
public static void Main(string[] args)
{
Row r = new Row { Id = 7, Name = "Cable, HDMI", Price = 12.5m };
Console.WriteLine(ToCsv(r));
}
}
提示与安全性
提示:
- 在热路径中优先使用 typeof 和缓存的元数据。
- 在调用 GetValue 或 Invoke 之前验证成员。
- 让辅助方法保持简短,并通过单元测试进行验证。
- 使用有帮助的消息快速失败。
反射准则
回顾
回顾:特性可以驱动小型的映射器、验证器、激活器和序列化器。让反射保持最少且安全。
常见问题解答
「轻量级元编程场景」课时是免费的吗?
是的 — 「轻量级元编程场景」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 3 节课。
「轻量级元编程场景」这节课中我会学到什么?
使用特性驱动的辅助工具:显示标签、必填验证、小型构造函数激活器,以及带有明确安全提示的类 CSV 序列化。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。
「轻量级元编程场景」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。