代码之家  ›  专栏  ›  技术社区  ›  Gup3rSuR4c

C如何为对象的每个属性构建表达式<func<t,u>>?

  •  1
  • Gup3rSuR4c  · 技术社区  · 6 年前

    我有个班 FormBuilder<TObject> 它有一个方法 Property<TProperty>(Expression<Func<TObject, TProperty>> expression) .在构造函数中,我希望在获取 TObject .我该怎么做呢?我现在使用的代码,是从其他的SO答案改编而来的,只是抛出错误,我不确定如何解决它们。这就是我目前为止所拥有的:

    public abstract class FormBuilder<TObject> :
        FormBuilder {
        public FormBuilder() {
            //  REPLICATE: Property(p => p.NAME);
    
            var method = this.GetType().GetMethod("Property");
            var properties = typeof(TObject).GetProperties();
    
            var objectType = typeof(TObject);
    
            foreach (var p in properties) {
                var propertyType = p.GetType();
    
                var parameter = Expression.Parameter(propertyType, "p");
                var property = Expression.Property(parameter, p.Name);
                var @delegate = typeof(Func<,>).MakeGenericType(objectType, propertyType);
    
                //  throws: ParameterExpression of type 'System.Reflection.RuntimePropertyInfo'
                //  cannot be used for delegate parameter of type 'UserQuery+Command'
                var expression = Expression.Lambda(@delegate, property, new[] { parameter });
    
                //  method.MakeGenericMethod(parameter).Invoke(this, new[] { property });
            }
        }
    
        public FormPropertyBuilder<TProperty> Property<TProperty>(
            Expression<Func<TObject, TProperty>> expression) {
            var member = (expression.Body as MemberExpression).Member;
            var builder = new FormPropertyBuilder<TProperty>(member.Name);
    
            Properties.Add(builder);
    
            return builder;
        }
    }
    

    编辑

    public sealed class CommandFormBuilder :
        FormBuilder<Command> {
        public CommandFormBuilder() {
            Property(
                p => p.SourceOfLoss).HasLabel("Source of Loss");
        }
    }
    

    这是我通常调用方法的方式,但我需要在为每个属性构造之后立即调用它。

    我的最终目标是通过该方法简单地对所有属性进行种子设定,并在以后进行进一步的配置(如果需要)。谢谢你的帮助!

    1 回复  |  直到 6 年前
        1
  •  1
  •   Gup3rSuR4c    6 年前

    我回来了!

    在@nkosi最后一次评论之后,我回到画板上从头开始。我太沉迷于仿制药了,一旦我退后一点,我就知道如何得到我想要的。不能说这是最合适的方法,但是现在我已经把它应用到我的整个应用程序中了,它似乎已经开始工作了。我最终制作了多个构建器(模板构建器?)对于特定任务,如窗体、表、PDF文档。这就是我想到的。我将只展示表单生成器,因为它对于所有其他生成器都是非常重复的,除了针对其场景的专用方法。这是自我发布问题以来经过多次迭代后的当前版本。哦,这个项目是在C 5上的vs 2013中构建的,所以一旦升级到新版本,就可能进行改进。

    行动

    public interface IFormActionBuilder {
        IFormActionBuilder HasFormId(
            string formId);
    
        IFormActionBuilder HasLabel(
            string label);
    
        IFormActionBuilder IsDefault();
    
        IFormActionBuilder IsFileUpload();
    }
    
    public interface IFormActionMetadata {
        string Controller { get; }
        bool Default { get; }
        string FormId { get; }
        string Enctype { get; }
        string Label { get; }
        string Name { get; }
    }
    

    public interface IFormGroupBuilder {
        IFormGroupBuilder HasOrder(
            short order);
    }
    
    public interface IFormGroupMetadata {
        string Label { get; }
        short Order { get; }
    }
    

    属性

    public interface IFormPropertyBuilder {
        IFormPropertyBuilder HasFormat(
            string format);
    
        IFormPropertyBuilder HasLabel(
            string label);
    
        IFormPropertyBuilder HasOrder(
            short order);
    
        IFormPropertyBuilder HasType(
            FormFieldType type);
    
        IFormPropertyBuilder InGroup(
            string group);
    
        IFormPropertyBuilder IsHidden();
    
        IFormPropertyBuilder IsReadOnly();
    
        IFormPropertyBuilder IsRequired();
    }
    
    public interface IFormPropertyMetadata {
        string Format { get; }
        string Group { get; }
        bool IsIgnored { get; set; }
        string Label { get; }
        string Name { get; }
        short Order { get; }
        bool Required { get; }
        FormFieldType Type { get; }
    }
    

    从技术上讲,操作、组和属性接口可以合并为一个接口,但我选择将它们拆分为方法和属性,以便在配置期间和提取元数据时保持IntelliSense的整洁。

    建设者

    public interface IFormMetadata {
        IList<IFormActionMetadata> ActionMetadatas { get; }
        IList<IFormGroupMetadata> GroupMetadatas { get; }
        IList<IFormPropertyMetadata> PropertyMetadatas { get; }
    }
    
    public abstract class FormBuilder<TObject> :
        IFormMetadata {
        public IList<IFormActionMetadata> ActionMetadatas { get; private set; }
        public IList<IFormGroupMetadata> GroupMetadatas { get; private set; }
        public IList<IFormPropertyMetadata> PropertyMetadatas { get; private set; }
    
        protected FormBuilder() {
            ActionMetadatas = new List<IFormActionMetadata>();
            GroupMetadatas = new List<IFormGroupMetadata> {
                new GroupBuilder()
            };
            PropertyMetadatas = typeof(TObject).GetProperties().Select(
                p => new PropertyBuilder(p.Name)).Cast<IFormPropertyMetadata>().ToList();
        }
    
        public IFormActionBuilder Action<TController>(
            Expression<Action<TController>> action)
            where TController : IController {
            var method = ((MethodCallExpression)action.Body).Method;
            var controller = typeof(TController).Name.Replace("Controller", null);
            var builder = new ActionBuilder(method.Name, controller);
    
            ActionMetadatas.Add(builder);
    
            return builder;
        }
    
        public IFormGroupBuilder Group(
            string label) {
            var builder = new GroupBuilder(label);
    
            GroupMetadatas.Add(builder);
    
            return builder;
        }
    
        public void Ignore<TProperty>(
            Expression<Func<TObject, TProperty>> expression) {
            var member = ((MemberExpression)expression.Body).Member;
            var propertyMetadata = PropertyMetadatas.SingleOrDefault(
                b => b.Name == member.Name);
    
            if (propertyMetadata == null) {
                return;
            }
    
            propertyMetadata.IsIgnored = true;
        }
    
        protected static T Placeholder<T>() {
            return default(T);
        }
    
        public IFormPropertyBuilder Property<TProperty>(
            Expression<Func<TObject, TProperty>> expression) {
            var member = ((MemberExpression)expression.Body).Member;
    
            return PropertyMetadatas.Single(
                b => b.Name == member.Name) as IFormPropertyBuilder;
        }
    
        public sealed class ActionBuilder :
            IFormActionBuilder,
            IFormActionMetadata {
            public string Controller { get; private set; }
            public bool Default { get; private set; }
            public string Enctype { get; private set; }
            public string FormId { get; private set; }
            public string Label { get; private set; }
            public string Name { get; private set; }
    
            public ActionBuilder(
                string action,
                string controller) {
                Controller = controller;
                Enctype = "application/x-www-form-urlencoded";
                FormId = "panel-form";
                Label = "Save";
                Name = action;
            }
    
            public IFormActionBuilder HasFormId(
                string formId) {
                FormId = formId;
    
                return this;
            }
    
            public IFormActionBuilder HasLabel(
                string label) {
                Label = label;
    
                return this;
            }
    
            public IFormActionBuilder IsDefault() {
                Default = true;
    
                return this;
            }
    
            public IFormActionBuilder IsFileUpload() {
                Enctype = "multipart/form-data";
    
                return this;
            }
        }
    
        public sealed class GroupBuilder :
            IFormGroupBuilder,
            IFormGroupMetadata {
            public string Label { get; private set; }
            public short Order { get; private set; }
    
            public GroupBuilder() {
            }
    
            public GroupBuilder(
                string label) {
                Label = label;
            }
    
            public IFormGroupBuilder HasOrder(
                short order) {
                Order = order;
    
                return this;
            }
        }
    
        public sealed class PropertyBuilder :
            IFormPropertyBuilder,
            IFormPropertyMetadata {
            private static readonly Type StringType;
    
            static PropertyBuilder() {
                StringType = typeof(string);
            }
    
            public string Format { get; private set; }
            public string Group { get; private set; }
            public bool IsIgnored { get; set; }
            public string Label { get; private set; }
            public string Name { get; private set; }
            public short Order { get; private set; }
            public bool Required { get; private set; }
            public FormFieldType Type { get; private set; }
    
            public PropertyBuilder(
                string name) {
                Name = name;
                Order = short.MaxValue;
                Required = !IsNullable(name);
                Type = FormFieldType.Text;
            }
    
            public IFormPropertyBuilder HasFormat(
                string format) {
                Format = format;
    
                return this;
            }
    
            public IFormPropertyBuilder HasLabel(
                string label) {
                Label = label;
    
                return this;
            }
    
            public IFormPropertyBuilder HasOrder(
                short order) {
                Order = order;
    
                return this;
            }
    
            public IFormPropertyBuilder HasType(
                FormFieldType type) {
                Type = type;
    
                return this;
            }
    
            public IFormPropertyBuilder InGroup(
                string group) {
                Group = group;
    
                return this;
            }
    
            public IFormPropertyBuilder IsHidden() {
                Type = FormFieldType.Hidden;
    
                return this;
            }
    
            private static bool IsNullable(
                string name) {
                var type = typeof(TObject).GetProperty(name).PropertyType;
    
                if (type == StringType) {
                    return true;
                }
    
                return type.IsValueType
                    && Nullable.GetUnderlyingType(type) != null;
            }
    
            public IFormPropertyBuilder IsReadOnly() {
                Type = FormFieldType.None;
    
                return this;
            }
    
            public IFormPropertyBuilder IsRequired() {
                Required = true;
    
                return this;
            }
        }
    }
    

    视图

    public sealed class FormView {
        public IList<Action> Actions { get; private set; }
        public IList<FormGroup> Groups { get; private set; }
    
        public FormView(
            object obj) {
            if (obj == null) {
                throw new ArgumentNullException("obj");
            }
    
            var type = obj.GetType();
            var builder = FormLoader.Get(type);
    
            if (builder == null) {
                var message = string.Format("Unable to find form builder for type: {0}", type.FullName);
    
                throw new InvalidOperationException(message);
            }
    
            Actions = GetActions(builder);
            Groups = GetGroups(obj, builder);
        }
    
        private static IList<Action> GetActions(
            IFormMetadata builder) {
            return builder.ActionMetadatas.Select(
                a => new Action {
                    Controller = a.Controller,
                    Enctype = a.Enctype,
                    FormId = a.FormId,
                    IsDefault = a.Default,
                    Label = a.Label,
                    Name = a.Name
                }).ToList();
        }
    
        private static object GetFieldValue(
            object obj,
            PropertyInfo property,
            IFormPropertyMetadata propertyMetadata) {
            var value = property.GetValue(obj, null);
    
            if (string.IsNullOrEmpty(propertyMetadata.Format)) {
                return value;
            }
    
            return string.Format(propertyMetadata.Format, value);
        }
    
        private static IList<FormField> GetFields(
            object obj,
            string group,
            IList<PropertyInfo> properties,
            IList<IFormPropertyMetadata> propertyMetadatas) {
            return properties.Select(
                p => {
                    var propertyMetadata = propertyMetadatas.SingleOrDefault(
                        pm =>
                            !pm.IsIgnored
                            && pm.Name == p.Name);
    
                    if (propertyMetadata == null) {
                        return null;
                    }
    
                    return new {
                        propertyMetadata.Group,
                        propertyMetadata.Label,
                        propertyMetadata.Name,
                        propertyMetadata.Order,
                        propertyMetadata.Required,
                        propertyMetadata.Type,
                        Value = GetFieldValue(obj, p, propertyMetadata)
                    };
                }).Where(
                a =>
                    a != null
                    && a.Group == group).OrderBy(
                a => a.Order).Select(
                a => new FormField {
                    IsRequired = a.Required,
                    Label = a.Label,
                    Name = a.Name,
                    Type = a.Type,
                    Value = a.Value
                }).ToList();
        }
    
        private static IList<FormGroup> GetGroups(
            object obj,
            IFormMetadata builder) {
            var properties = obj.GetType().GetProperties();
    
            return builder.GroupMetadatas.Select(
                g => new {
                    g.Label,
                    g.Order,
                    Fields = GetFields(obj, g.Label, properties, builder.PropertyMetadatas)
                }).OrderBy(
                a => a.Order).Select(
                a => new FormGroup {
                    Label = a.Label,
                    Fields = a.Fields
                }).ToList();
        }
    
        public sealed class Action {
            public string Controller { get; set; }
            public string Enctype { get; set; }
            public string FormId { get; set; }
            public bool IsDefault { get; set; }
            public string Label { get; set; }
            public string Name { get; set; }
        }
    }
    

    装载机

    public static class FormLoader {
        private static IDictionary<Type, Builder> _builders;
    
        public static IFormMetadata Get<TObject>() {
            return Get(typeof(TObject));
        }
    
        public static IFormMetadata Get(
            Type type) {
            var builder = _builders[type];
    
            if (builder.Instance == null) {
                builder.Instance = Activator.CreateInstance(builder.Type) as IFormMetadata;
            }
    
            return builder.Instance;
        }
    
        public static void Initialize() {
            Initialize(Assembly.GetExecutingAssembly());
        }
    
        public static void Initialize(
            params Assembly[] assemblies) {
            var type = typeof(FormBuilder<>);
    
            _builders = assemblies.SelectMany(
                a => a.GetTypes()).Where(
                t =>
                    !t.IsAbstract
                    && !t.IsInterface
                    && t.BaseType != null
                    && t.BaseType.IsGenericType
                    && t.BaseType.GetGenericTypeDefinition() == type).Select(
                t => new {
                    ObjectType = t.BaseType.GetGenericArguments()[0],
                    BuilderType = t
                }).ToDictionary(
                k => k.ObjectType,
                v => new Builder {
                    Type = v.BuilderType
                });
        }
    
        private sealed class Builder {
            public IFormMetadata Instance { get; set; }
            public Type Type { get; set; }
        }
    }
    

    我不太喜欢装载机。它可以工作,但是我为每种构建器类型都有一个加载程序,所以我只是重复了很多。前几天我花了几个小时试图把它们合并成一个,但没能找到任何地方,于是又恢复了原状。两者的主要区别在于它们改变了接口。如果有人对改进加载器和/或任何其他代码有建议,我很高兴听到他们的建议。

    我有一个特殊的例子,两个构建器需要继承一个基本对象的构建器,所以它们有一个 IncludeBase<TObjectBase> 方法。我对这件事不太自信,但它起作用了。

    public void IncludeBase<TObjectBase>() {
        var baseBuilder = DocumentLoader.Get<TObjectBase>();
    
        if (baseBuilder == null) {
            return;
        }
    
        foreach (var basePropertyMetadata in baseBuilder.PropertyMetadatas) {
            var propertyMetadata = PropertyMetadatas.SingleOrDefault(
                pm => pm.Name == basePropertyMetadata.Name);
    
            if (propertyMetadata == null) {
                continue;
            }
    
            propertyMetadata = basePropertyMetadata;
        }
    }
    

    这就是问题所在。感谢你的阅读,如果你能做到这一点,我要感谢@nkosi把我从我自己陷入的陷阱中解救出来。