【发布时间】:2019-01-13 04:17:42
【问题描述】:
我正在尝试创建自定义序列化程序。它需要高性能。
想法是为每种类型构造和缓存一些Func<…>
在这个简化的示例中,我成功为 STRING 类型构造了 Func,但我一直不知道如何为 ARRAY 类型构造它。
想象一下,我现在可以序列化 Meow 类,但我无法序列化 Ruff 类,这可能会有所帮助。
class Program
{
class Meow
{
public string Rawr { get; set; } = "X";
}
class Ruff
{
public Meow[] Grr { get; set; } = new[] { new Meow(), new Meow() };
}
static class Serializer<T>
{
static int DoSomething(string value, Stream stream) => value.Length;
static int DoSomethingElse(T[] values, Stream stream) => values.Length;
public static Func<T, Stream, int> GetSerializer()
{
var firstProperty = typeof(T).GetProperties()[0].GetGetMethod();
var typeParam = Expression.Parameter(typeof(T));
var compiledGetter = Expression
.Lambda(
Expression.Call(typeParam, firstProperty),
typeParam
)
.Compile();
var returnType = firstProperty.ReturnType;
if (returnType == typeof(string))
{
var getString = (Func<T, string>)compiledGetter;
return (T item, Stream stream) => DoSomething(getString(item), stream);
}
if (returnType.IsArray)
{
// var getArray = (Func<T, returnType>)compiledGetter;
var elementType = returnType.GetElementType();
// return (T item, Stream stream) =>
// Serializer<elementType>.DoSomethingElse(getArray(item), stream))
}
return (T item, Stream stream) => 0;
}
}
static void Main(string[] args)
{
MemoryStream s = new MemoryStream();
Console.WriteLine(Serializer<Meow>.GetSerializer()(new Meow(), s));
Console.WriteLine(Serializer<Ruff>.GetSerializer()(new Ruff(), s));
Console.ReadKey();
// Should print "1", "2"
// Currently prints "1", "0"
}
}
序列化Meow 很容易。该函数将获取T 和Stream,从T 中提取string,并将它们传递给DoSomething(string, Stream) 以返回一个布尔值。
但是在序列化Ruff 时,它遇到了一个返回类型为Meow[] 的属性。序列化它需要取T和Stream,从T中提取一个未知元素类型的数组,并将它们传递给Serializer<Meow>.DoSomethingElse(Meow[], Stream)
注释掉的行显示了我认为需要发生的事情的要点。但是我怎样才能为这一切创建一个已编译的Expression 并最终返回一个Func<T, Stream, bool>?
编辑:现在包含测试代码。实现时,Ruff 序列化程序应该输出2,即数组的长度。
编辑 #2:解决方案! 感谢 Jeff Mercado
下面是工作代码(只是 GetSerializer 方法)
public static Func<T, Stream, int> GetSerializer()
{
var itemTypeExpression = Expression.Parameter(typeof(T));
var streamTypeExpression = Expression.Parameter(typeof(Stream));
var firstProperty = typeof(T).GetProperties().First();
var propType = firstProperty.PropertyType;
var getterExpression = Expression.Lambda(
Expression.Property(itemTypeExpression, firstProperty),
itemTypeExpression
);
Expression body = null;
if (propType == typeof(string))
{
body = Expression.Call(
typeof(Serializer<T>),
nameof(DoSomething),
Type.EmptyTypes,
Expression.Invoke(
getterExpression,
itemTypeExpression
),
streamTypeExpression
);
}
else if (propType.IsArray)
{
var elementType = propType.GetElementType();
var elementTypeExpression = Expression.Parameter(elementType);
var serializerType = typeof(Serializer<>).MakeGenericType(elementType);
var serializerTypeExpression = Expression.Parameter(serializerType);
body = Expression.Call(
serializerType,
nameof(DoSomethingElse),
Type.EmptyTypes,
Expression.Invoke(
getterExpression,
itemTypeExpression
),
streamTypeExpression
);
}
if (body != null)
return Expression.Lambda<Func<T, Stream, int>>(body, itemTypeExpression, streamTypeExpression).Compile();
return (T item, Stream stream) => 0;
}
【问题讨论】:
-
您能否在序列化
Meow(显示输出)时显示示例输入代码以及您的代码工作?然后显示尽可能多的代码来序列化Ruff以及预期的输出等? -
你为什么要使用表达式来构建它?当您在编译时不知道类型时,表达式很有用。这就是你在这里所期待的吗?这是一个通用的序列化器吗?
-
@Enigmativity 是的,一个有点通用的序列化程序。它将根据特殊规则序列化几十个模型类。我可以为每个定义一个序列化程序,但这会很痛苦并且难以维护。至于输入代码: Serializer
.GetSerializer()(new Meow(), null) -
请使用输入代码编辑您的问题,以便我可以复制、粘贴和运行它。然后告诉我你期望的输出。请花点时间来做这件事,这样我就不必自己弄清楚了。那么这意味着我可以专注于给你答案而不是编写测试代码。
标签: c# lambda reflection expression