【问题标题】:Using class properties to limit function parameter values使用类属性限制函数参数值
【发布时间】:2016-05-26 21:07:07
【问题描述】:

简单类:

public class myTest
{
    public class Person
    {
        public Int32 Id { get; set; }
        public String Fname { get; set; }
        public string Lname { get; set; }
    }

    public enum EPerson
    {
        Id=0,
        Fname=1,
        Lname=2
    }

    public static void Get(EPerson SearchBy, dynamic Value)
    {
        var Sql = "select from dbo.bobo where " + SearchBy.ToString() + "='" + Value + "'";

    }

    public static void testget()
    {
        Get(EPerson.Fname, "Bob");
    }
}

IntelliSense 适用于此。我的 Get 方法显示所有三个枚举值供我选择一个。

除了在 Get 方法中使用枚举之外,我如何通过查询 Person 属性来做同样的事情,从而消除对用于 IntelliSense 目的的枚举的需要?

【问题讨论】:

  • 我建议您阅读有关如何使用诸如实体框架之类的 ORM 并完全避免所有 SQL 语句的构建。
  • 啊。我建议你坚持这个话题。这与 SQL 无关。这只是描述我想要完成的事情的一种简单方式。
  • 那么也许你应该准确地解释你在问什么,当有人试图帮助时不要那么生气。
  • 这个问题看起来像是一个要求知道如何将列名传递给方法的请求,但您的评论暗示情况并非如此。你的意图是什么?
  • 哦,天哪。我应该在这里剪切并粘贴我的问题还是重新输入? “而不是在 Get 方法中使用枚举,我如何通过查询 Person 属性来做同样的事情......?”

标签: c# function parameters enums


【解决方案1】:

您可以使用 lambda 表达式通过智能感知等进行编译类型检查。

但是,您实际上只能接受 lambda 表达式的特定子集,并且不能对其进行编译时检查。

private static Person GetPerson(Expression<Func<Person, object>> selectorExpression)
{
    var body = selectorExpression.Body as UnaryExpression;
    var operand = body.Operand as BinaryExpression;
    var left = operand.Left as MemberExpression;
    var right = operand.Right as ConstantExpression;

    string propertyName = left.Member.Name;
    object value = right.Value;

    return /* get person where "propertyName" == "value" here */
}

现在您可以像这样调用 GetPerson:

var person = GetPerson(p => p.Name == "Fred");

请注意,lambda 表达式必须完全采用 p =&gt; p.Property == constantValue 的形式。

另外注意:我没有在GetPerson中实现任何错误检查,所以错误的格式会导致它在运行时崩溃。

【讨论】:

  • 这个答案真的很好,我不知道你可以这样使用lambda表达式。
  • 我知道这一点来到这里,希望现在有更好的方法。
【解决方案2】:

您可以做的是使用具有以下功能的反射:

private List<String> GetProperties ( Type Model )
{
    var props = new List<MemberInfo> ( Model.GetFields ( BindingFlags.Public | BindingFlags.Instance ) );
    props.AddRange ( Model.GetFields ( BindingFlags.NonPublic | BindingFlags.Instance ) );
    props.AddRange ( Model.GetProperties ( BindingFlags.Public | BindingFlags.Instance ) );
    props.AddRange ( Model.GetProperties ( BindingFlags.NonPublic | BindingFlags.Instance ) );

    var names = new List<String> ( );
    props.ForEach ( prop => names.Add ( prop.Name ) );

    return names;
}

这将获取所有类的字段和属性 (the difference),无论它们是公共的还是私有的。

它的作用

  1. 获取模型类型作为参数;
  2. 获取所有属性和字段并将它们添加到列表中;
  3. 将它们作为 List&lt;String&gt; 以及所有属性和字段名称返回。

用法

要获取属性和字段名称:

var props = GetProperties ( typeof ( Person ) )

然后检查传入的参数是否在列表中:

if ( props.Contains ( SearchBy ) )
{
    // Do something...
}

【讨论】:

  • 这与我所做的非常相似,但使用该方法并没有提高 IntelliSense 的可能值。看起来 Enum 是这样做的唯一方法。
  • @Chris 看起来 Blorgbeard 的 answer 可能是你最好的选择。
【解决方案3】:

如果你想限制一个方法在编译时接收的参数,你总是必须有一个枚举(至少到目前为止 C# 没有办法获取属性列表在编译时。

但是,如果您可以在运行时中限制输入,您可以使用反射来获取属性列表并检查输入以查看它是否应该返回或抛出以及异常/返回错误代码。值得一提的是,反射,所以请确保您确实需要它,否则性能不是问题。

另一方面,如果您需要编译时检查,但希望能够查询对象的属性值(而不是数据库,因为您的 Get 方法似乎正在做),您可以像已经完成的那样使用枚举并使用反射通过枚举获取属性值,这样您就知道要获取哪个属性的值,但我建议在这种情况下使用Dictionary 来存储值并使用使用枚举的类型安全属性,因此您拥有“两全其美”。你仍然有枚举,但如果你需要编译时间检查它是你能做的最好的。 这是此方法的示例实现(没有完整性检查):

public class myTest
{
    public class Person
    {
        protected Dictionary<string, object> _properties;

        public Person()
        {
            _properties = new Dictionary<string, object>();
        }

        public Int32 Id 
        { 
            get 
            { 
                return (int)_properties[EPerson.Id];
            } 
            set
            {
                _properties[EPerson.Id] = value;
            }
        }
        public String Fname 
        { 
            get 
            { 
                return _properties[EPerson.FName] as String;
            } 
            set
            {
                _properties[EPerson.FName] = value;
            }
        }
        public string Lname
        { 
            get 
            { 
                return _properties[EPerson.LName] as String;
            } 
            set
            {
                _properties[EPerson.LName] = value;
            }
        }
    }

    public enum EPerson
    {
        Id = 0,
        Fname = 1,
        Lname = 2
    }

    public static void Get(EPerson SearchBy, dynamic Value)
    {
        var Sql = "select from dbo.bobo where " + SearchBy.ToString() + "='" + Value + "'";
    }

    public static void testget()
    {
        Get(EPerson.Fname, "Bob");
    }
}

【讨论】:

  • 是的,C# 有一种在编译时获取属性的方法,请查看我的答案。
  • 正如我所说,如果您不需要编译时检查,则使用反射(这是您的答案详细解释的内容),但如果您需要编译器实际阻止您传递参数这不是一个类的属性,你需要使用我的答案中的黑客攻击。
  • 哦,抱歉,我没有意识到这一点。
  • @Bikonja:我试图完全摆脱枚举,看看在使用该方法时是否有其他东西可以代替它用于 IntelliSense。看起来 Enum 是使 IntelliSense 正常工作的唯一方法。
【解决方案4】:

听起来您想创建一种方法,允许您查找具有匹配 id、名字或姓氏的 Person 实例,而无需使用 enum 指定搜索类型。

有几种方法可以做到这一点:

  1. 每种搜索类型的单独方法。如果您想通过 ID 找到它们,那么您可以使用 GetPersonById(int)' method. By first name would beGetPersonByFName(string)'。
  2. 使用默认参数。您的方法签名将是public Person GetPerson(int? id = null, string fName = null, string lName = null)。在该方法中,您将使用非空参数进行搜索。如果全部为 null,那么您将返回完整的对象列表或抛出异常(确保在摘要标记中指定该方法的工作方式)。
  3. 与#2 类似,但使用Person 作为唯一参数。用户需要创建一个新的 Person 实例来进行搜索。
  4. bool 参数指示哪些列与Value 中的值进行比较。不建议这样做。

【讨论】:

  • 不,他想要的是能够在智能感知中使用Person属性进行自动补全
  • @Chris 好。如果您需要有关其中一个选项的更多详细信息,请告诉我。否则this 应该会帮助您完成下一步。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 2023-01-20
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 2012-05-10
  • 2017-09-14
相关资源
最近更新 更多