I have a profile form that has a lot of user selections and I am sort of stumped on a good approach to validate what the user enters, when passing validation mapping those values to object properties.
For example I have a dictionary
public static Dictionary<string, string> objProfileSelections = new Dictionary<string, string>();
public static string MySelections(string key)
{
objProfileSelections.Add("1", "No Answer");
objProfileSelections.Add("3", "Less Than $25,000");
objProfileSelections.Add("5", "$35,000 to $50,000");
objProfileSelections.Add("7", "$50,000 to $75,000");
objProfileSelections.Add("9", "$75,000 to $100,000");
objProfileSelections.Add("11", "$100,000 to $150,000");
objProfileSelections.Add("13", "$150,000+");
objProfileSelections.Add("2", "No Answer");
objProfileSelections.Add("4", "Less Than $25,000");
objProfileSelections.Add("6", "$35,000 to $50,000");
objProfileSelections.Add("8", "$50,000 to $75,000");
objProfileSelections.Add("10", "$75,000 to $100,000");
objProfileSelections.Add("12", "$100,000 to $150,000");
objProfileSelections.Add("14", "$150,000+");
string item;
objProfileSelections.TryGetValue(key, out item);
return item;
}
Id like to pass in a list of key strings from the user and pass those items to populate an object. The issue is I don't know how to code it so it know which property to go to, I looked at reflection, but I couldn't find any examples that have a set dictionary of values that map to property names.
To make a bit more clear, when a user makes a selection it passes as a parameter in the dictionary, and the dictionary outputs the items. From key 1 comes value No Answer. If the user selected all the check boxes it would be value - (1,3,5,7,9,11,13). I need to extract those values when there is a matching key to a matching property. For example if the user clicks 1,5 but leaves the rest unchecked, how do I know which selections the user made? How do I get the program to know which property to populate based on the results?
*edit
some properties I would like it mapped to
public string MyAnnualIncome{ get; set; }
public List<string> InterestAnnualIncome{ get; set; }
So the first property would be taking one value, and the second property would be taking multiple values.
When a key matches a value comes out the dictionary, I would need the odd values going to MyAnnualIncome and the even values going to InterestAnnualIncome.
so no one is confused odd and even keys are set up for a purpose, odd numbers belonging to a certain group of properties and the even ones belonging to another based on the html selections (even being my selections, odd being what I am interested in)
*Update
Is there a way I can possibly use the keys like 1,3,5 and pass that into a list using the except extension method. Then take the results and use a method to convert the values from enumerated data types to strings?
Hopefully I understood your question.
I would add a small helper class (this is a solution which doesn't use reflection, but uses delegates instead):
public class PropertyModifier
{
private string text;
private Func<string> modifier;
public PropertyModifier(Func<string> modifier)
{
this.modifier = modifier;
}
public PropertyModifier With(string text)
{
PropertyModifier newModifier = new PropertyModifier(modifier);
newModifier.text = text;
return newModifier;
}
public void Modify()
{
modifier(Text);
}
}
Then I would rewrite your code and have the dictionary map to this class instead to string:
public static Dictionary<string, PropertyModifier> objProfileSelections = new Dictionary<string, PropertyModifier>();
public static MyUserProfile Profile; //Assuming this is the object you want to modify
public static string MySelections(string key)
{
PropertyModifier myIncome = new PropertyModifier(text => Profile.MyAnnualIncome = text);
PropertyModifier interestIncome = new PropertyModifier(text => Profile.InterestAnnualIncome.Add(text));
objProfileSelections.Add("1", myIncome.With("No Answer"));
objProfileSelections.Add("3", myIncome.With("Less Than $25,000"));
...
objProfileSelections.Add("2", interestIncome.With("No Answer"));
objProfileSelections.Add("4", interestIncome.With("Less Than $25,000"));
...
}
Then, when processing the user's selection, get the mapped PropertyModifier from the dictionary and call its Modify method.
I tried in this code to illustrate how you can modify the properties of the different classes that may compose a profile. Modifications are done by reflection only, i.e. just providing the class name, the property name that will vary in each class and the string value to be assigned to the property.
Not sure that it fits your expectations :(
Profile profile = new Profile() ;
profile.SetPropertyValue("hair","color","brown") ;
internal class Profile()
{
private Hair hair_ = new Hair();
private Job job_ = new Job ();
internal Hair hair { get { return hair_ ; } }
internal Job job { get { return job_ ; } }
private void SetPropertyValue(string profileItemName, string ItemPropertyName, string value)
{ // it is assumed that the different items (hair or job) of the Profile are accessible
// with a a property
// first find the Item object, i.e. hair or job
object itemObj = this.GetType().GetProperty(profileItemName).GetValue(this,null);
// assign to Item property the input value, e.g. hair.color=Brown
itemObj.GetType().GetProperty(ItemPropertyName).SetValue(itemObj, value, null);
}
}
internal class Hair()
{
private string color_ ;
private string style_ ;
internal string color { get { return color_ ; } set {color_ = value ; } }
internal string style { get { return style_ ; } set {style_ = value ; } }
}
Related
I try to explain my question by using simplified source code.
The class I have is e.g.:
public class House
{
public House(...)
{
address = ...;
owner = ...;
}
public string address; //Unique variable
public string owner; //Not unique variable
}
At some point, I need a dictionary which has the "House" as key and e.g. a boolean as value.
E.g.
var doesTheHouseOwnerHaveDept= new Dictionary<House,bool>();
Then, I have the problem that the dictionary "doesTheHouseOwnerHaveDept" is - of course - full of duplicates since, by considering the address and owner, multiple unique "key pairs" exist if a person owns multiple houses.
Thus, is there a possibility to modify the class such that only the "owner" variable within the class "house" is used for specifying the key of the dictionary "doesTheHouseOwnerHaveDept"?
I.e., when an owner e.g. "Max" owns the house at address "A" and "B", then, first come, first serve like, only one "House"-instance will be added to the dictionary "doesTheHouseOwnerHaveDept".
I know that in the previous example, the problem could easily be solved in other more intuitional ways, but I did not have a better idea and wanted to avoid posting original source code.
Thanks a million for your support and effort! :)
If you want the owner (in this simplified code) to be the Key of your Dictionary you will need to override Equals and GetHashCode. It is important to override both otherwise it will not work.
Here an example of the House class:
If you create two houses with the same owner and try to add them to a dictionary where the Key is the House object it would give you an error
Edit
Here an importand edit from #l33t:
"Do not use a public field. Instead use a property with a private setter. Any value used in GetHashCode() must be immutable or your objects will get lost (in e.g. a dictionary), never to be found again."
public class House
{
public House(string address, string owner)
{
this.Address = address;
this.Owner = owner;
}
public string Address; //Unique variable
public string Owner
{
get;
private set; //Private setter so the owner can't be changed outside this class because it if changes and the object is already inside
// a dictionary it won't get notified and there will be two objects with the same 'Key'
}
public override bool Equals(object obj)
{
if (!(obj is House)) return false;
var toCompare = (House) obj;
return this.Owner == toCompare.Owner; //Just compare the owner. The other properties (address) can be the same
}
public override int GetHashCode()
{
return Owner.GetHashCode(); //Just get hashcode of the owner. Hashcode from the address is irrelevant in this example
}
One of our projects makes use of key-value pairs where certain runtime parameters - that do not change per instance of the program - determine the value gotten. For example:
Program run in test mode with the parameter "Municipal":
Key: "testMunicipalDirectory"
Value: "C:\Foo\Bar\"
Program run with the parameter "State":
Key: "StateDirectory"
Value: "C:\Bar\Baz\"
To make it slightly more complicated, if there is no matching key for, say "testMunicipalImagesDirectory", there is a fallback of "defaultImagesDirectory":
Key: "testMunicipalImagesDirectory" ?? "defaultImagesDirectory"
Value: "C:\Foo\Bar\Images" ?? "C:\Images"
Currently, there's a lot of code duplication/inefficiencies, and room for error. Every time one of these is referenced there's string concatenation and null-coalescing and other stuff going on.
It seems like this would benefit from a single-instance object that is passed certain parameters on initialization (test or not, "State" or "Municipal", etc), that will return the correct values for each different property the keys represent.
Many answers I found to questions asking how to use the singleton design pattern with parameters basically boil down to "if it uses parameters, you probably do not want a singleton". In my case, it is invalid to attempt to initialize the object with different values, and an exception should be thrown if this happens.
This is how I would accomplish this goal (pseudo-C#) (lazy-loading is not a requirement but is used here):
public sealed class Helper
{
// how can we enforce that Init has been called?
private static readonly Lazy<Helper> lazyLoader = new Lazy<Helper>(() => new Helper(name, test));
public static Helper Instance { get { return lazyLoader.Value; } }
public static void Init(string name, bool test)
{
// if it has already been initalized
throw new InvalidOperationException("This has already been initalized.");
// else initalize it
}
private string Name { get; set; }
private bool Test { get; set; }
private Helper(string name, bool test) { } // assign to properties, any other ctor logic
public string Directory
{ get { return ValueGetter.Get((this.Test ? "test" : "") + this.Name + "Directory"); } }
}
public static class ValueGetter
{
public static string Get(string key, string fallbackKey)
{
if (Keys.Any(k => k == key))
return Keys[key].Value;
else
return Keys[fallbackKey].Value;
}
}
But as you can see, there are questions remaining. How can it enforce calling Init before using the Instance, but not require those parameters to be passed every time Instance is accessed?
Is this the correct direction to go, or is there a better design pattern to use?
I implemented Custom Attributes for enum using this article, everything is fine with hard coding values, but I need to pass the parameters in run time, for example:
enum MyItems{
[CustomEnumAttribute("Products", "en-US", Config.Products)]
Products
}
The Config.Products (bool value) is the problem, the error is:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
Is there any way to fix this?
Update
The enum (MyItems in this case) have 20 items, each item must have that custom attribute, then I want to generate menu from the Enum's items, depended on Culture I getting the matched title, also depended on Config, I decided to show/hide the item from the menu (in fact if Config.X == false, I don't add the item to the menu)
Also, for Config, I have another system and I wanna sync that system with the menu, that is the reason that I wanna get the Config.X in run-time.
Thanks!
No way to fix this, it's a limitation of attributes.
You can use static readonly fields in case you need a fixed set of objects with behaviour:
public class MyItems
{
public string Name { get; private set; }
public string Locale { get; private set; }
readonly Func<OtherThing> factory;
public static readonly MyItems Products = new MyItems("Products", "en-US", () => Config.Products);
public static readonly MyItems Food = new MyItems("Food", "en-GB", () => Config.FishAndChips);
private MyItems(string name, string locale, Func<OtherThing> factory)
{
this.Name = name;
this.Locale = locale;
this.factory = factory;
}
public OtherThing GetOtherThing() {
return factory();
}
}
See another answer for a more complete example:
C# vs Java Enum (for those new to C#)
You could create an extension method
public string GetConfigValue(this MyItems myItem)
{
return Config.GetType().GetProperty(myItem.ToString()).GetValue(Config, null);
}
This uses reflection to access the relevant property on the Config object. In the example you give, if myItem = Products then you can call
myItem.GetConfigValue()
And it should return the value of Config.Products
Related SO Questions:
Dynamic Evaluation
Enum Name
Based on your update, I'd suggest this even more. Attributes must be constant values at compile time (hence the error you're getting). Even if you do not go the extension method route, you absolutely need some sort of method.
Scenario:
i have a web form from where i m taking input for Item class now i want to assign values to feature that have return type of list how can i do that.
item value = new item(),
value.feature = serialtextbox.text; //error
foreach ( var item in value) //error
{
item.SerialNo= serialtextbox.text;
}
Item and Item feature classes
Class Item
{
list<Itemfeature> features;
}
class ItemFeature
{
public int SerialNo
{
get { return serialno; }
set { serialno = value; }
}
public int Weight
{
get { return weight; }
set { weight = value; }
}
}
Plz help me out
Note: No language is specified, but it looks like C#. I'm assuming C# in this answer.
It's not really clear what you're trying to do here, but I'll give it a shot. First of all, you're going to want to post the actual code you're using. This code won't even compile, it's loaded with syntax errors.
Let's take a look at your objects first:
class Item
{
List<ItemFeature> features;
}
class ItemFeature
{
public int SerialNo
{
get { return serialno; }
set { serialno = value; }
}
public int Weight
{
get { return weight; }
set { weight = value; }
}
}
You have a custom class, ItemFeature, which consists of a serial number (integer) and a weight (integer). You then have another custom class, Item, which consists of a list of ItemFeatures.
Now it looks like you're trying to add a new ItemFeature to the Item and then loop through all of them and set them again?. Something like this, perhaps?:
Item value = new Item();
value.features.Add(new ItemFeature { SerialNo = int.Parse(serialtextbox.Text) } );
foreach (var item in value.features)
{
item.SerialNo = int.Parse(serialtextbox.Text);
}
(Note that this code is probably as free-hand as your code, so I haven't tested it or anything.)
What I've changed here is:
Setting the SerialNo property, rather than trying to set the ItemFeature directly to a value. You need to dig into the object's property to set a value on that property, not just set it to the entire object.
Converting the input (a string) into the property's type (an int).
Looping through the list, not the Item object itself. The Item object contains a list as a property, but the object itself isn't a list. You can loop through the property, not through the parent object.
A few things to ask/note:
What exactly are you trying to do? You have a list of objects, but you're only setting one and then looping through that one to set it again. Why?
You may want to consider more apt class/property names. Things like "Item" can be a bit unclear.
Your Item class has a public variable, features. This is generally frowned upon. It's better to use a property. That way if you ever have to add logic behind it you won't break compatibility outside of the object itself. The ItemFeature class has properties like this, which is good. They can be additionally shortened by using automatic properties if you'd like, just to keep things clean and simple.
Note that my code isn't doing any input checking on the serialtextbox.Text value. It should be. I presented it in a simpler form as an introductory approach to something that will work under ideal conditions. But something like the following would be better:
var serialValue = 0;
if (!int.TryParse(serialtextbox.Text, out serialValue))
{
// Here you would probably present an error to the user stating that the form field failed validation.
// Maybe even throw an exception? Depends on how you handle errors.
// Mainly, exit the logic flow.
return;
}
var value = new Item();
value.features.Add(new ItemFeature { SerialNo = serialValue } );
Edit: I just noticed that my call to .Add() will actually fail. You'll want to initialize the list before trying to use it. Consider changing the Item class to something like this:
class Item
{
public List<ItemFeature> features { get; set; }
public Item()
{
features = new List<ItemFeature>();
}
}
Two things changed here:
I converted the public member to a property, as previously mentioned.
I added a constructor which initializes the list so that it can be used. Otherwise, being a reference type, it would default to null. So any call to .Add() or any other method on the list would throw a NullReferenceException because there's no object on which to call the method(s).
I have an application where I need to populate a textbox with a company name and I have filled a custom AutoCompleteStringColection with all the available company names from the database. When a user enters changes the company name by typing and selecting from the list a new company name I need to have the id (Guid), of the selected company so I can do a lookup and get the rest of the company information. Because the company name is not guaranteed to be unique I cannot do a lookup on the name and expect to have the right record. I looked at extending the string class, but all I can find are examples that add methods. I tried that by adding a variable to store the id and methods to get and set the id, but when retrieving the id it is always the last id set. Can a property be added to a class by extending it? I have already changed what I was trying to do to do a lookup on the company name and display a list the user will choose from if multiple matches are returned, but I would still like to know if I can add a property this way in case it comes up again.
No, you cannot extend classes with properties. Additionally, String is sealed so you can't extend it by inheriting. The only recourse is to composition: encapsulate string in your own class.
It sounds like you should create your own class:
class Company {
public string Name {get;set;}
public override string ToString() {return Name;}
// etc
}
Now bind to a set of Company objects; the ToString override will ensure that the Name is displayed by default, and you can add whatever else you need. For more complex scenarios, you can use (for example) DisplayMember and ValueMember (of a combo-box) to point at different properties (rather than the default ToString).
You should use a ComboBox rather than a TextBox. Create a custom type that has your company name and id in it, making sure that it overrides ToString to return the company name. Add those custom types to the ComboBox rather than straight-up strings, and use AutoCompleteSource of ListItems.
I used Konrad's answer and for the sake of completeness I am posting my solution here. I needed to show my user an autocomplete list of company names, but since they could have multiple companies with the same name I needed the Guid id to find their choice in the database. So I wrote my own class inheriting from AutoCompleteStringCollection.
public class AutoCompleteStringWithIdCollection : AutoCompleteStringCollection
{
private List<Guid> _idList = new List<Guid>();
/*-- Properties --*/
public Guid this[int index]
{
get
{
return _idList[index];
}
}
public Guid this[string value]
{
get
{
int index = base.IndexOf(value);
return _idList[index];
}
}
/*-- Methods --*/
public int Add(string value, Guid id)
{
int index = base.Add(value);
_idList.Insert(index, id);
return index;
}
public new void Remove(string value)
{
int index = base.IndexOf(value);
if (index > -1)
{
base.RemoveAt(index);
_idList.RemoveAt(index);
}
}
public new void RemoveAt(int index)
{
base.RemoveAt(index);
_idList.RemoveAt(index);
}
public new void Clear()
{
base.Clear();
_idList.Clear();
}
}