У вас есть пара проблем: при построении выражения ваш тип Ruff
не будет удовлетворять этому выражению.
Вы эффективно строите это выражение:
(Rawr arg0, Stream arg1) =>
Serializer<Ruff>.DoSomethingElse(arg0.Grr, arg1);
Обратите внимание, что тип arg0.Grr
равен Meow[]
, но ожидаемый тип Ruff[]
. Вместо этого метод DoSomethingElse()
должен быть универсальным, чтобы быть совместимым.
static int DoSomethingElse<TValue>(TValue[] values, Stream stream) => values.Length;
С другой стороны, кажется, что на самом деле не имеет значения, что является базовым типом, вам просто нужна длина массива. Так что вместо этого вы можете просто сделать его Array
, и он все равно будет работать.
static int DoSomethingElse(Array values, Stream stream) => values.Length;
В целом, я бы не стал смешивать различные типы выражений таким образом (объекты выражений и лямбда-выражения), ни строить их все, используя лямбда-выражения, либо все, используя выражения. Я бы написал этот метод так:
public static Func<T, Stream, int> GetSerializer()
{
var firstProperty = typeof(T).GetProperties().First();
var item = Expression.Parameter(typeof(T));
var stream = Expression.Parameter(typeof(Stream));
var propType = firstProperty.PropertyType;
if (typeof(string).IsAssignableFrom(propType))
{
var body = Expression.Call(
typeof(Serializer<T>),
"DoSomething",
Type.EmptyTypes,
Expression.Invoke(
MakeGetter(firstProperty),
item
),
stream
);
return Expression.Lambda<Func<T, Stream, int>>(body, item, stream).Compile();
}
if (typeof(Array).IsAssignableFrom(propType))
{
var body = Expression.Call(
typeof(Serializer<T>),
"DoSomethingElse",
Type.EmptyTypes,
Expression.Invoke(
MakeGetter(firstProperty),
item
),
stream
);
return Expression.Lambda<Func<T, Stream, int>>(body, item, stream).Compile();
}
return (T arg0, Stream arg1) => 0;
Expression MakeGetter(PropertyInfo prop)
{
var arg0 = Expression.Parameter(typeof(T));
return Expression.Lambda(
Expression.Property(arg0, prop),
arg0
);
}
}
Исходя из ваших комментариев, для меня имеет смысл сделать методы обобщенными, а не сериализатором. Вам просто нужно будет сделать соответствующие выражения для общих вызовов.
static class Serializer
{
static int DoSomething(string value, Stream stream) => value.Length;
static int DoSomethingElse<T>(T[] values, Stream stream) => values.Length;
public static Func<T, Stream, int> GetSerializer<T>()
{
var firstProperty = typeof(T).GetProperties().First();
var item = Expression.Parameter(typeof(T));
var stream = Expression.Parameter(typeof(Stream));
var propType = firstProperty.PropertyType;
if (typeof(string).IsAssignableFrom(propType))
{
var body = Expression.Call(
typeof(Serializer),
"DoSomething",
Type.EmptyTypes,
Expression.Invoke(
MakeGetter(firstProperty),
item
),
stream
);
return Expression.Lambda<Func<T, Stream, int>>(body, item, stream).Compile();
}
if (typeof(Array).IsAssignableFrom(propType))
{
var body = Expression.Call(
typeof(Serializer),
"DoSomethingElse",
new[] { propType.GetElementType() },
Expression.Invoke(
MakeGetter(firstProperty),
item
),
stream
);
return Expression.Lambda<Func<T, Stream, int>>(body, item, stream).Compile();
}
return (T arg0, Stream arg1) => 0;
Expression MakeGetter(PropertyInfo prop)
{
var arg0 = Expression.Parameter(typeof(T));
return Expression.Lambda(
Expression.Property(arg0, prop),
arg0
);
}
}
}