In my application I work with criterias. I have one base Criteria interface and and other interfaces who inherits from this base interface:
ICriteria
|
|
----------------------
| |
ITextCriteria IChoices
What I’d like to know is, what is the best way to know what Type the class is?
In my code I have a dropdown box and based on that I have to determine the type:
// Get selected criteria
var selectedCriteria = cmbType.SelectedItem as ICriteria;
if (selectedCriteria is IChoices)
{
//selectedCriteria = cmbType.SelectedItem as IChoices; Doesn't work
IChoices criteria = selectedCriteria as IChoices;//cmbType.SelectedItem as IChoices;
SaveMultipleChoiceValues(criteria);
//_category.AddCriteria(criteria);
}
else
{
//ICriteria criteria = selectedCriteria; //cmbType.SelectedItem as ICriteria;
if (selectedCriteria.GetCriteriaType() == CriteriaTypes.None)
{
return;
}
//_category.AddCriteria(criteria);
}
_category.AddCriteria(selectedCriteria);
selectedCriteria.LabelText = txtLabeltext.Text;
this.Close();
My question is, is this the best way? Or is there a better way to achieve this?
The chance is big that there are coming more interfaces based on ICriteria.
EDIT:
I have 2 types of controls which I want to add dynamically to my application. One control is a textbox and the other is a radio button.
For a radio button the user can define the options. When the options are defined, the user must choose one of the options and the chosen option must be saved in the database (this is later used to perform search operations). So, when the Save button is clicked, I have to determine the chosen type (radio or text) and save the answer possibilities (if it is a radio).
For a textbox, this doesn’t have any answer possibilities. For that reason it has a different interface.
I hope I make it a little bit clearer now. Here is another question which is related: C# How to implement interface where concrete classes differs?
EDIT II:
This is how my method SaveMultipleChoiceValues looks like:
private void SaveMultipleChoiceValues(IChoices criteria)
{
foreach (DataGridViewRow row in dgvCriteriaControls.Rows)
{
if (row == dgvCriteriaControls.Rows[dgvCriteriaControls.Rows.Count - 1])
continue;
//multipleChoice.AddChoice(row.Cells["Name"].Value.ToString());
string choice = row.Cells["Name"].Value.ToString();
criteria.AddChoice(choice);
}
}
This looks like a prime example for polymorphism.
Instead of trying to do a type switch on your
ICriteriaimplementation, why don’t you add a method toICriteria(or possibly a virtual method to some common base class of allICriteriaimplementations), and just call that?Obviously the implementation of this method would need access to objects that do not belong in your
ICriteriainstances, but that is a problem you can solve using other design patterns according to the specifics of your scenario.Update:
Here’s a complete solution, incorporating the code you posted:
Create a new interface
ICriteriaViewwhich models the view (in your case aForm) whereICriteriaare displayed. The form needs to do some processing depending on the exact interface that criteria implement, so add a method with one overload for each interface that exists in your code. Do not add an overload forICriteriaitself. [1]Your form will implement this interface, providing methods where suitable processing for each subtype of
ICriteriawill occur:Each implementation of
ICriteriawill need to implement a method which calls the appropriateICriteriaViewoverload for its type. This is where the “redirection magic” happens: we will use polymorphism to get the compiler to “discover” the actual type ofICriteriaour object is, and then use method overloading onICriteriaView.ProcessCriteriato access the appropriate code.And this is where the dispatch to the appropriate overload happens:
Then, your code would do:
Maintenance:
Whenever you add a new
ICriteriasub-interface implementation, the definition ofICriteriawill force you to implement thePerformProcessingOnmethod on it. Inside that method, all you can do really is callview.ProcessCriteria(this). In turn, this will force you to implement an appropriateProcessCriteriaoverload inICriteriaViewandMyForm.As a result, we have achieved two important objectives:
ICriteriaimplementation without specifying exactly how that implementation should interact withICriteriaView.MyViewdoes with e.g.IChoiceswhen reading the code forMultipleChoice. The structure of the code leads you toMyForm.SaveMultipleChoiceValues“automatically”.Notes:
[1] The choice of adding an overload for
ICriteriaitself or not is really a tradeoff:If you do add one, then code like this:
will compile successfully always, because even if there is no
ICriteriaView.ProcessCriteria(IChoices)overload there will still be theICriteriaView.ProcessCriteria(ICriteria)overload that the compiler can use.This means that, when adding a new
ICriteriasub-interface implementation, the compiler will no longer force you to go check if the implementation ofICriteriaView.ProcessCriteria(ICriteria)really does the right thing for your new implementation.If you do not add one, then the moment you write
view.ProcessCriteria(this);the compiler will force you to go check (and update)ICriteriaViewandMyFormaccordingly.In this scenario, and with the information you have provided, I believe that the appropriate choice would be the last one.
[2] As you can see above, the implementation of
ICriteria.PerformProcessingOninsideMultipleChoiceandSimpleInputlooks exactly the same. If these two classes have a common base (which is quite possible in practice), you might be tempted to move the “duplicated” code into that base. Do not do that; it will cause the solution to break.The tricky part is that inside
MultipleChoice, when you doview.ProcessCriteria(this);the compiler can infer that the static type ofthisisIChoices— this is where the redirection happens! If you move the call toProcessCriteriainside a hypothetical base classCriteriaBase : ICriteria, then the type ofthiswill becomeICriteriaand the dispatch of the call to the appropriateICriteriaView.ProcessCriteriaoverload will no longer work.