sebastienros / fluid

Fluid is an open-source .NET template engine based on the Liquid template language.
MIT License
1.44k stars 178 forks source link

Supporting `struct` by default, would be great #556

Closed schittli closed 1 week ago

schittli commented 1 year ago

Good evening

thanks a lot for sharing your great work!

I tried to use fluid with c# struct objects and it looks like fluid usually always expects to get class objects, for example, TemplateOptions.Default.MemberAccessStrategy.Register<T> is declared with: where T : class

I guess, that it would be great if fluid would support structs, by default, too.

Thanks a lot, kind regards, Thomas

mhehle commented 3 months ago

using a struct anywhere will throw System.ArgumentException Cannot bind to the target method because its signature is not compatible with that of the delegate type

System.ArgumentException Cannot bind to the target method because its signature is not compatible with that of the delegate type.
   at System.Reflection.RuntimeMethodInfo.CreateDelegateInternal(Type delegateType, Object firstArgument, DelegateBindingFlags bindingFlags)
   at Fluid.Accessors.PropertyInfoAccessor..ctor(PropertyInfo propertyInfo)
   at Fluid.MemberAccessStrategyExtensions.<>c__DisplayClass1_0.<GetTypeMembers>b__0(ValueTuple`2 key)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Fluid.MemberAccessStrategyExtensions.GetTypeMembers(Type type, MemberNameStrategy memberNameStrategy)
   at Fluid.MemberAccessStrategyExtensions.Register(MemberAccessStrategy strategy, Type type)
   at Fluid.UnsafeMemberAccessStrategy.GetAccessor(Type type, String name)
   at Fluid.Values.ObjectValueBase.GetValueAsync(String name, TemplateContext context)
   at Fluid.Ast.IdentifierSegment.ResolveAsync(FluidValue value, TemplateContext context)
   at Fluid.Ast.MemberExpression.EvaluateAsync(TemplateContext context)
   at Fluid.Ast.OutputStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.Parser.FluidTemplate.RenderAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TemplateContext context, TextEncoder encoder, Boolean isolateContext)
Brunni commented 3 weeks ago

What is the workaround here? I want to use units from the UnitsNet library and those are all structs.

sebastienros commented 3 weeks ago

I found a workaround, here is an example with Point which is a struct:

            var options = new TemplateOptions();
            options.MemberAccessStrategy.Register<Shape>();
            options.MemberAccessStrategy.Register<Point>(nameof(Point.X), new DelegateAccessor<Point, int>((point, name, context) => point.X));
            options.MemberAccessStrategy.Register<Point>(nameof(Point.Y), new DelegateAccessor<Point, int>((point, name, context) => point.Y));

            var circle = new Shape
            {
                Coordinates = new Point(1, 2)
            };

            var template = _parser.Parse("{{Coordinates.X}} {{Coordinates.Y}}");
            Assert.Equal("1 2", template.Render(new TemplateContext(circle, options, false)));