I've been struggling for a while to find a way of calling an instance of a class from a user defined input.
Essentially I want to call an instance from my StandardVehicle class that has already been defined, however the problem is that the name of the instance that I'm calling is defined by a users selection on a combo box on a form.
This is how I assumed it would be formatted, however I get an error when I try calling the instance saying that I cannot complicitly convert from string to StandardVehicle.
StandardVehicle VauxhallB = new StandardVehicle();
VauxhallB.Model = "B";
VauxhallB.Manufacturer = "Vauxhall";
VauxhallB.Doors = 5;
VauxhallB.HorsePower = 200;
VauxhallB.Transmission = "Manual";
VauxhallB.Year = 2009;
VauxhallB.Cylinders = 6;
VauxhallB.Seats = 7;
VauxhallB.Price = 17000;
VauxhallB.Registration = "abc1243";
StandardVehicle objname = comboBox1.Text;
I'm assuming it's just a case of formatting the string, but I've been searching for hours and can't figure out what format it needs to be in to define it as an instance.
Any help would be appreciated.
Regards,
S.
No, it's definitely not a case of just formatting the string. It sounds like you want a
Dictionary<string, StandardVehicle>
Populate that to map from strings to objects, then look up the object corresponding to the name given by the user.
VauxhallB is just a symbol to the computer. The user shouldn't reference your variable names -- although it is probably possible to do so via reflection, but that's a complicated aspect of programming.
A better way to do this would be to associate each instance with a string "name" in a Dictionary<string, StandardVehicle>, and look up with that. Something like:
Dictionary<string, StandardVehicle> nameMap = new Dictionary<string, StandardVehicle>();
nameMap["VauxhallB"] = VauxhallB;
StandardVehicle objname = nameMap[comboBox1.Text];
Also, it's generally more readable if you only use lower case names for local variables, to better distinguish them from classes, public methods, and properties.
You cannot convert string to an object unless you have
Constructor that takes string and creates required object
An explicit/implicit cast operator
and another option
Use Activator.CreateInstace
3rd option is only valid if string contains valid type name, for your example you can do
Type type = Type.GetType(comboBox1.Text);
StandardVehicle objname = Activator.CreateInstace(type);
this will work if comboBox1.Text == "StandardVehicle".
More information here:
http://msdn.microsoft.com/en-us/library/w3f99sx1.aspx
http://msdn.microsoft.com/en-us/library/wccyzw83.aspx
Try this:
StandardVehicle selectedObject = (StandardVehicle)comboBox1.SelectedItem;
Of course, your comboBox should be bound to a collection of StandardVehicles... If it's not, then go for Dictionary as was already suggested.
First I also thought you mean reflection but you want to use your class StandardVehicle always. Just the properties should be set by a single string you receive from the user.
Write yourself a constructor. You define the format of the single string, for example fixed position within that string:
public StandardVehicle(String data)
{
this.Model = data.Substring(0, 1);
this.HorsePower = Int32.Parse(data.Substring(1, 4);
}
Not very comfortable but might work. Add some error handling. Or XML loading or other formats.
It's a little more complicated than your sample. I'm going to start with the quick and easy approach, which, while IS quick and easy, is kind of an anti-pattern, but I 'll explain that at the end:
IBritishAuto car;
switch
{
case "Vauxhall":
{
car = new Vauxhall(standardVehicle);
break;
}
case "Jaguar":
{
car = new Jaguar(standardVehicle);
}
...etc.
}
The reason why this is sub-optimal is that every time you want to add a new car type, you have to modify the original code block, and risk regression. If you're happy with this solution, and don't care, you can stop reading now.
Still there?
This pattern is called a "conditional dispatcher". There are two main strategies to improve it: refactor to a Command pattern (that's capped because it's a Gang of Four pattern), or a broader implementation of a concept called 'inversion of control'. If you're interested, here are your Google terms:
"refactor conditional dispatcher"
"inversion of control".
Good luck.
Related
I am not sure how to even title or ask this question, so apologies for any confusion.
I need to get the value of the Id from the first list in Model.Content.GetPropertyValue("SlidePanel"). I've tried many, many, many thing with my troubles usually being that "You can't do this because it's an Object". In the image below, I want to get the Id Value of "1092" as a String.
-----EDIT-------
I was able to get the value with the code below. Casted the list, grabbed the first list since it would always be that option (I wrapped it in an if, but removed it in this example), then I was able to specify the property I needed and converted as needed.
If I sound like I don't speak this language fluently, it's because I'm still fresh to development. Thanks for everyone who helped.
dynamic slidePanelObject = Model.Content.GetProperty("SlidePanel").Value;
List<object> slidePanelCast = ((IEnumerable<object>)slidePanelObject).Cast<object>().ToList();
dynamic slidePanelFirst = slidePanelCast.First();
var slidePanelId = slidePanelFirst.Id;
string slidePanelString = slidePanelId.ToString();
Model.Content.GetPropertyValue is probably returning an System.Object, so you'll need to cast your temp var to a List<T> type of some kind before you can access it like a list.
Without knowing all the types involved, here's some code that you could modify:
var temp = Model.Content.GetPropertyValue("SlidePanel") as List<TYourType>;
This question is connected (but NOT duplicate) of this one. Consider that I am fairly new to C#.
I would like, if possible, to take the handle of a member of an object that has not been instantiated jet, in order to use it later on. Following previous question, I would like to do something like
List<Action<YourClass>> lst = new List<Action<YourClass>>;
lst.Add(x => x.Member1);
lst.Add(x => x.Member2);
Member1 and Member2 are not supposed to be static member, as their value depend on the state of the object they are member of. Basically I want the handle of their "name", so that I can use it later on when the objects are instantiated. I was thinking about an approach based on string which value is the member name, but maybe there is a better way?
Thanks.
I'm not sure if i understand you right. At first you need to create an instance from your list.
List<Action<YourClass>> lst = new List<Action<YourClass>>;
Else your Add will broke with NullReference-Exception. What you are using in your Add is called anonymus function because the handle isn't saved. If you would like to store this you need a delegate. On this delegate you can call the Invoke-Methode to call it. You are allowed to create your on delegates as well as using predefined like Action.
Here is a small example without any sence, but maybe clarify:
var action = new Action<string>(x => x = x.Substring(1, 1));
//Do some other stuff
action.Invoke("Hallo");
Note that the used var keyword. It detects the result of new Action and take the type of this. In this case it holds Action.
Further note that Action is a predefined delegate. An other would be Func which got one return value. If you need other behaviour you easily can create your own delegates. For this you should read the link.
I found the solution thanks to Henk comment:
Func<myObj, Vector> getVect = new Func<myObj, Vector>
getVect= (myObj => myObj.objVector);
where objVector is NOT a method, but a member of the myObj.
I call getVect in this way:
Vector a= getVect(someObj)
This won't make a lot of sense so apologies in advance, I'm not a C# developer but have been given some code to look at.
I need to pass a model object in to a function, however I looping through and so will need to build the name dynamically. So lets assume I would normally reference it like so.
model.employee.title
model.employee.forename
How can I reference it using a string for the last part.
I'm using a foreach to loop through the various parts which is fine, but I then need to pass the object in to a function call. I have the name of the last part as a literal string ("title","forname") but I'm not sure how I pass that in.
So effectively what I need is to use something like
model.employee."title"
or
model.employee.varContainingTheName
I know these won't work, but have no idea how to proceed.
Basically, if you have a string of the member name, you're talking about reflection. The reflection API differs between fields and properties, so you'd need to know which it is, for example (where obj is model.employee, and name is the name of the member you want to access):
object value = obj.GetType().GetField(name).GetValue(obj);
vs:
object value = obj.GetType().GetProperty(name).GetValue(obj, null);
Alternatively, use an API like FastMember (which incidentally is much faster than raw reflection - it uses lots of voodoo):
var wrapper = ObjectAccessor.Create(obj);
object value = wrapper[name];
If you develop the model.employee class and use it like that, you should consider using dictionary for fileds.
class employee
{
public Dictionary<string, object> properties = new Dictionary<string,object>();
public employee()
{
properties.Add("time", DateTime.Now);
properties.Add("name", "Bill Gates");
}
}
and then use it like this:
Employee employee = new Employee();
employee.properties["time"];
this code will be faster and easier to read.
First off, I am new to programming (especially with C#) and thanks for your help.
I have a static web form with about 150 form objects (most checkboxes). I decided to go 1 record per form submission in the sql db. So, for example, question X has a choice of 5 checkboxes. Each of these 5 checkboxes has a column in the db.
I have the post page complete(working) and am building an edit page where I load the record and then populate the form.
How I am doing this is by passing a stored proc the id and then putting all the returned column values into the according object properties, then setting the asp control object to them.
An example of setting the asp controls to the selected value:
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
As you see, this is very tiresome since there are over 150 of these to do. Also, I just found out if the response is NULL then I get the error that it cant be converted to a string. So, I have added this line of code to get past it:
This is the part where I am populating the returned column values into the object properties (entity is the object):
if (!String.IsNullOrEmpty((string)reader["questionX0"].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX0"];}
So, instead of having to add this if then statement 150+ times. There must be a way to do this more efficiently.
First of all, it seems that you are using string.IsNullOrEmpty(value), but this won’t check for the special DBNull value that is returned from databases when the data is null. You should use something more akin to value is DBNull.
The rest of your problem sounds complex, so please don’t be put off if my answer is complex too. Personally I would use custom attributes:
Declare a custom attribute
The following is a skeleton to give you the idea. You may want to use the “Attribute” code snippet in Visual Studio to find out more about how to declare these.
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class QuestionColumnAttribute : Attribute
{
public string ColumnName { get; private set; }
public QuestionColumnAttribute(string columnName)
{
ColumnName = columnName;
}
}
Use the custom attribute in the entity class
Where you declare your entity class, add this custom attribute to every field, for example where patientUnderMdTreatment is declared:
[QuestionColumn("questionX0")]
public string patientUnderMdTreatment;
Iterate over the fields
Instead of iterating over the columns in the reader, iterate over the fields. For each field that has a QuestionColumnAttribute on it, get the relevant column from the reader:
foreach (var field in entity.GetType().GetFields())
{
var attributes = field.GetCustomAttributes(typeof(QuestionColumnAttribute), true);
if (attributes.Length == 0)
continue;
object value = reader[attributes[0].ColumnName];
if (!(value is DBNull))
field.SetValue(entity, value.ToString());
}
For the first part of your question where you set the ASP controls, you can use a similar strategy iterating over the fields of selectedForm, and this is probably simpler because you don’t need a custom attribute — just take only the fields whose name starts with “questionX”.
this is a quick & easy way of doing it.. there are some suggestions to investigate LINQ, and I'd go with those first.
for (int i = 0; i < 150; i++)
{
if (!String.IsNullOrEmpty((string)reader["questionX" + i.ToString()].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX" + i.ToString()];}
}
... though this wouldn't be any good with the
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
lines
so I hear two questions:
- how to deal with null coming from IDataReader?
- how to deal with multiple fields?
Lets start with simple one. Define yourself a helper method:
public static T IsDbNull<T>(object value, T defaultValue)
{
return (T)(value is DBNull ? defaultValue : value);
}
then use it:
entity.patientUnderMdTreatment = IsDbNull<string>(reader["question"], null);
Now how to map entity fields to the form? Well that really is up to you. You can either hardcode it or use reflection. The difference of runtime mapping vs compile-time is likely to be completely irrelevant for your case.
It helps if your form fields have identical names to ones in the DB, so you don't have to do name mapping on top of that (as in Timwi's post), but in the end you'll likely find out that you have to do validation/normalization on many of them anyway at which point hardcoding is really what you need, since there isn't a way to dynamically generate logic according to the changing spec. It doesn't matter if you'll have to rename 150 db fields or attach 150 attributes - in the end it is always a O(n) solution where n is number of fields.
I am still a little unsure why do you need to read data back. If you need to preserve user's input on form reload (due to validation error?) wouldn't it be easier/better to reload them from the request? Also are entity and selectedForm the same object type? I assume its not a db entity (otherwise why use reader at all?).
Its possible that there are some shortcuts you may take, but I am having hard time following what are you reading and writing and when.
I recommend using the NullableDataReader. It eliminates the issue.
I'm facing a problem that I don't know how to solve and am hoping the community can help.
I'm writing an app that manages "Lead" objects. (These are sales leads.) One part of my program will import leads from a text file. Now, the text file contains lots of potential leads, some of which I will want to import and some of which I won't.
For ease of programming (and use), I'm parsing the text file into a List<Lead> object, and using a DataGridView to display the leads by setting the DataSource property of the DataGridView.
What I want to do is add a column to the grid, called "Import," with a checkbox that the user can check to indicate whether or not each lead should be imported.
My first thought is to derive a class from Lead:
public Class LeadWithImportCheckbox : Lead
{
bool bImport = false;
public bool Import
{
get { return bImport;}
set { bImport = value;}
}
}
However, the parsing engine returns a list of Lead objects. I can't downcast a Lead to a LeadWithImportCheckbox. This fails:
LeadWithImportCheckbox newLead = (LeadWithImportCheckbox)LeadFromParsingEngine;
This is an invalid cast.
The other option I see is to create a constructor for LeadWithImportCheckbox:
public LeadWithImportCheckbox(Lead newlead)
{
base.Property1 = newlead.Property1;
base.Property2 = newlead.Property2;
....
base.Property_n = newlead.Property_n;
}
This is problematic for two reasons. One, the Lead object has several dozen properties and writing this constructor is a PITA.
But worse, if I ever change the underlying structure of Lead, I need to remember to go back and change this constructor for LeadWithImportCheckbox. This is a danger to my code maintenance.
Is there a better way of accomplishing my goal?
or, to avoid the PITA aspect, use reflection... (try this...)
EDIT: use property, not Field as I had originally written...
public class NewLead : Lead
{
public bool Insert;
public NewLead(Lead lead, bool insert)
{
Insert = insert;
foreach (PropertyInfo pi in typeof(Lead).GetProperties())
GetType().GetProperty(pi.Name).SetValue
(this, pi.GetValue(lead,null), null);
}
}
public class LeadListItem
{
public Lead Lead { get; set; }
public bool ShouldImport { get; set; }
}
i.e. don't copy the Lead object's contents, just store a reference to it in a new LeadListItem object, which adds extra info "outside" the original object.
If you want the properties of Lead to appear in the grid, there is almost certainly a way of doing that. Why not ask that question, instead of downvoting me for telling you the right answer to this question!
A couple options you might have missed:
You could update the Lead object itself to have an Import property (that defaults to false).
You could have your "ImportLead" object treat the Lead as payload (even make it generic, if you want), so you don't need the big constructor.
Build a new Lead object list or enumerable that only contains the objects you want to import in the first place.
You can only downcast, if the object to be downcast is really an object of that type.
An easier way to solve your problem would be to have a DisplayLead class, such as:
public class DisplayLead {
Lead lead;
bool bImport;
}
which would also help you separating stored data from their representation in a GUI.
What you want to do is display the checkbox column on your grid and not have it related at all to your Lead objects. You use the marked columns (and possible the original List) to build a new set of List which will be your import list.
Then handle whatever you wish to do with the newly created List.
Edit: One thing to be careful of when working with lists is the fact every class object is actually only a pointer to the class so if you work with the original list and do something like:
List<Lead> Importable = new List<Lead>();
for(int i=0, i++, i<viewGrid.Count)
if(viewGrid[i].CheckedColumn.Checked)
Importable.Add(OriginalList[i]);
That objects will exist in both lists and if you edit data of a Lead on either list both will be changed.
I cannot downcast to something it is not. If the object was instantiated as a Lead, then it can't be downcast to any derived class. If it were instantiated as a LeadWithImportCheckbox and then returned to your code as Lead, then you can downcast it.
Protip: Check type at runtime with is operator.
There are many ways to do this, but the "right" way pops out because of what you said, here:
For ease of programming (and use), I'm
parsing the text file into a
List object, and using a
DataGridView to display the leads by
setting the DataSource property of the
DataGridView.
What I want to do is add a column to
the grid, called "Import," with a
checkbox that the user can check to
indicate whether or not each lead
should be imported.
Your Lead object stands well on its own, and you want to attach some metadata to it -- you don't want to create another Lead classification (i.e. the LeadWithImportCheckbox class).
So, the best approach in your case is to have a class like so:
public class LeadInfo
{
private Lead lead;
private bool shouldImport;
public LeadInfo(Lead lead)
{
this.lead = lead;
this.ShouldImport = false;
}
public bool ShouldImport
{
get { return shouldImport; }
set { shouldImport = value; }
}
}
This will scale well when you want to add more metadata to your list, like if you want to send yourself email reminders about them every week.
I've seen the correct solution listed so many times I feel like a heel posting it again, but the best way to approach this is to write a wrapper for the Lead object that includes the import flag.
If the properties of the Lead object don't appear in the GridView because you're databinding to the object, then write passthrough properties that mirror the Lead properties on the wrapper object.
The issue is that you want something displayed to the user that isn't an inherent part of the data model. The answer is to wrap the data before presenting it to the user so you can control what they see without changing the underlying model.
If you're concerned that the Lead object will change so many times in the future that changes to the wrapper will be cumbersome, you could look into dynamic code generation based on the Lead object that will automatically generate a wrapper object with the same fields as the Lead object plus the import flag. Though frankly, that's a lot more work than you'll probably need for something as straightforward as this.
As a quick and dirty solution, you can create your 'checkbox' object as a different object that contains an instance of Lead.
public GridLead {
public bool Import { get; set; }
public Lead Lead { get; set; }
}
This way you can easily add more 'grid' properties to this object, while still always retaining a reference to the Lead details without hardcoding property cloning into it.
Recommend you try modifying (upgrading) your imported lead objects.
Try starting with the examples here...
If your Lead class had a copy constructor (e.g. "Lead(Lead otherLead)"), LeadWithImportCheckbox would inherit that and you could just call the base Lead constructor in the LeadWithImportCheckbox constructor - hence no need for LeadWithImportCheckbox to be aware of the details of Lead.