0

Simple class:

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 works for this. My Get method displays all three enum values for me to select one.

Instead of using an enum in the Get method, how do I do the same thing by querying the Person properties thereby eliminating the need for the Enum for IntelliSense purposes?

Chris
  • 650
  • 7
  • 20
  • 5
    I suggest you go read up on how to use an ORM such as Entity Framework and avoid all the building up of SQL statements altogether. – DavidG May 26 '16 at 21:10
  • 1
    Argh. I suggest you stick to the topic. This has nothing to do with SQL. It was just a simple way to describe what I'm trying to accomplish. – Chris May 26 '16 at 21:16
  • Then perhaps you should explain exactly what it is you are asking and not get so angry when someone is trying to help. – DavidG May 26 '16 at 21:18
  • The question looks like a request to know how to pass a column name into a method, but your comment implies that this is not the case. What is your intent? – Trisped May 26 '16 at 21:19
  • Oh, my. Should I cut and paste my question here or just type it again? "Instead of using an enum in the Get method, how do I do the same thing by querying the Person properties...?" – Chris May 26 '16 at 21:19
  • I am not quite clear about what you want to accomplish. Are you saying that you want your `Get` method to display a pop-up of your Person properties while writing code to consume the `Get` method? – Brian Payne May 26 '16 at 21:20
  • Ok...really. How about this for a method? public string showIt(EPerson s) – Chris May 26 '16 at 21:20
  • Your question is not clear, that's why I said that. So yeah, try again. – DavidG May 26 '16 at 21:23
  • KMC...that's what I don't know how to do as such. What type of variable is the SearchBy parameter? I can do this: foreach (var prop in typeof (Person).GetProperties()) and get the property names. But where am I storing those such that IntelliSense displays them when I consume the method? – Chris May 26 '16 at 21:25
  • so if the properties is a subset of the table dbo.bobo, then you can use reflection. someone already posted a helpful link below. Since this looks like a small app, I'm not going to debate on enum vs. string vs. other abstractions. – dfdsfdsfsdf May 26 '16 at 21:31
  • If what you want is to use another class' properties and fields on autocomplete for intellisense, that's not possible unless you use a plugin. But if what you want is to filter the SearchBy values based on another class' properties and fields I suggest you take a look at my answer below. – GGG May 26 '16 at 21:33
  • @DavidG: my question was very clear if you had taken the time to read the subject of the question: "Using class properties to limit function parameter values". – Chris May 27 '16 at 00:08
  • Yes of course, that's why 5 people upvoted my initial comment. – DavidG May 27 '16 at 00:09

4 Answers4

3

You can use a lambda-expression to get compile-type type checking with intellisense, etc.

However, you can only realistically accept a specific subset of lambda expressions, and there can be no compile-time checking of that.

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 */
}

Now you can call GetPerson like this:

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

Note that the lambda expression must be exactly of the form p => p.Property == constantValue.

Also note: I have not implemented any error checking in GetPerson, so the wrong format will cause it to crash at runtime.

Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
2

What you could do is use Reflection with the following function:

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;
}

which will get all class' fields and properties (the difference) be them public or private.

What it does

  1. It gets the model type as a parameter;
  2. Gets all properties and fields and adds them to a list;
  3. Returns them as a List<String> with all properties' and fields' names.

Usage

To get the properties and fields names:

var props = GetProperties ( typeof ( Person ) )

And then to check if the passed parameter is in the list:

if ( props.Contains ( SearchBy ) )
{
    // Do something...
}
Community
  • 1
  • 1
GGG
  • 640
  • 1
  • 9
  • 27
  • This is really similar to what I was doing but consuming the method did not raise IntelliSense with the possible values. It looks like Enum is the only way to do that. – Chris May 26 '16 at 21:42
  • @Chris looks like Blorgbeard's [answer](http://stackoverflow.com/a/37471437/2671392) might be your best call then. – GGG May 26 '16 at 21:44
1

If you want to limit the parameters that a method receives in compile time you will always have to have an enum (at least so far C# doesn't have a way to get the list of properties at compile time.

However, if you are okay with limiting the input in run time, you could use reflection to get the list of properties and check the input to see whether it should return or throw and exception/return error code. It is worth mentioning that reflection is slow so be sure you really need it or performance isn't a problem.

If, on the other hand, you need the compile time check, but would like to be able to query the object for the property values (not the database as your Get method seems to be doing), you could use an enum as you have already done and use reflection to get property values via the enum so you know which property's value to get, but I would recommend in that case to use a Dictionary to store the values and use type-safe properties that use the enum so you have "the best of both worlds". You still have the enum, but if you need the compile time check it's the best you can do. Here is an example implementation (without sanity checks) of this method:

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");
    }
}
Bikonja
  • 909
  • 5
  • 15
  • Yes, C# has a way of getting properties in compiletime, just check my answer. – GGG May 26 '16 at 21:37
  • As I stated, if you don't need compile-time checks then use reflection (which is what your answer explains in detail), but if you need the compiler to actually stop you from passing a parameter that is not a property of a class, you need to use hacks like the one in my answer. – Bikonja May 26 '16 at 21:39
  • Oh sorry, I didn't realize that. – GGG May 26 '16 at 21:39
  • @Bikonja: I was trying to get rid of the enum completely and see if something else could replace it for IntelliSense purposes when the method was used. It looks like Enum is the only way to make IntelliSense work properly. – Chris May 26 '16 at 22:07
0

It sounds like you want to make one method which allows you to find the Person instances which have a matching id, first name, or last name without using the enum to specify the search type.

There are a few ways to do this:

  1. Separate methods for each search type. If you want to find them by ID, then you would use the GetPersonById(int)' method. By first name would beGetPersonByFName(string)'.
  2. Use default parameters. You method signature would be public Person GetPerson(int? id = null, string fName = null, string lName = null). In the method you would use the non-null arguments to do your search. If all are null, then you would return the full list of objects or throw an exception (make sure you specify how the method works in the summary tag).
  3. Similar to #2, but with a Person as the only argument. The user would be required to create a new Person instance to do the search.
  4. bool arguments indicating which columns to compare to the value in Value. This is not recommended.
Trisped
  • 5,705
  • 2
  • 45
  • 58
  • No, what he wants is to be able to use `Person` properties in intellisense for autocompleting – GGG May 26 '16 at 21:50
  • @Chris Good. Let me know if you need more detail on one of the options. Otherwise [this](https://stackoverflow.com/help/someone-answers) should help you with your next steps. – Trisped May 26 '16 at 21:56