I have a method for filtering/searching which now is written for every Windows Form that offers searching. I'm trying to make a generic method in the base class so I can avoid this large amount of repeating code.
Here is the original code which I want to modify:
private void LoadData()
{
GridFilter filter = new GridFilter();
filter.AddRule(dgvColDescription.DataPropertyName, txtDescription.Text);
if (cboColor.SelectedIndex != -1)
{
filter.AddRule(dgvColMaterialColorId.DataPropertyName, cboColor.SelectedValue.ToString());
}
...
//a lot more of this filter.AddRule stuff
...
}
There are two things here I think - the need of instance for GridFilter and the two types of control that I need to use.
Having said that here is my try for a generic method:
protected virtual void AddFilterRules<T>(Control ctrl, String str) where T : GridFilter
{
T filter;
if (ctrl is ComboBox)
{
if ((ctrl as ComboBox).SelectedIndex != -1)
{
filter.AddRule(/*dgvColMaterialColorId.DataPropertyName*/ str, (ctrl as ComboBox).SelectedValue.ToString());
}
}
if (ctrl is TextBox)
{
filter.AddRule(/*dgvColCode.DataPropertyName*/str, ctrl.Text);
}
}
There are some comments because I'm still not sure if I can pass the commented part as string, but the bigger problem for me is the error that I get from the IDE about the filter variable which is:
Use of unassigned local variable "filter"
Is it possible to make a generic method out of it after all and how can I do that?
Add the new() contraint to your method.
protected virtual void AddFilterRules<T>(Control ctrl, String str) where T : GridFilter, new()
and then
T filter = new T();
Either that or pass T in as a parameter:
protected virtual void AddFilterRules<T>(T filter, Control ctrl, String str) where T : GridFilter
Your syntax is correct, but you have two other problems:
You are not initializing T to anything. It is a null reference at the point you are trying to use it. Did you intend to pass in a T?
You're not doing anything with the T. Should you be returning the T? Or assigning it to a property of some other object?
If you pass in a reference to a T and then modify it within the method, then both problems are solved:
protected virtual void AddFilterRules<T>(T filter, Control ctrl, String str)
where T : GridFilter
{
...
}
However in that case your method doesn't need to be generic at all:
protected virtual void AddFilterRules(GridFilter filter, Control ctrl, String str)
{
...
}
Why do you want to use a generic method?
If your filter is always a GridFilter you can just use that type in your method. Even if you use a derived class like SpecialGridFilter this would still work. The fact that in your original method you create a new GridFilter each time means that you can do so in your new method. Hence there is no need for a new method despite the fact that you pass some values to it now.
It looks like you mix up type parameters with ordinary parameters. Having a type parameter doesn't mean you get an instance of that type. It just means you can use the same algorithms for different types without explicitly coding them for each type.
If you really need a generic implementation you can use a type constraint on the generic parameter where T : GridFilter, new(). This allows you to create a new T() in your generic method.
Where will the new method be implemented?
I don't know how GridFilter is implemented now but it looks like AddRule modifies it. If so you shouldn't create a new instance each time because all changes will get lost. AddFilterRules should therefore also be a member of the GridFilter class and each form should have an instance of a GridFilter.
If you cannot change the implementation of GridFilter you can still use an extension method. If it is correct to create a new GridFilter each time you can have an extension method on the Form type. This depends on how GridFilter works.
You should create an instance of T:
T filter = new GridFilter();
If T is always going to be a GridFilter, you may as well replace T by GridFilter:
protected virtual void AddFilterRules<GridFilter>(Control ctrl, String str)
{
GridFilter filter = new GridFilter();
...
}
However, I don't think you should use a generic method at all. You should create a method that returns a GridFilter:
protected virtual GridFilter AddFilterRules(Control ctrl, String str)
{
GridFilter filter = new GridFilter();
...
return filter;
}
Related
I'm working with a project for thesis-work for a company and I'm having some difficulties understanding some code.
In their code they have a line like this
_subscriber.StartSubscribing(_messageHandler.HandleMessage);
where _subscriber is function is defined
public override void StartSubscribing(Action<QueueItem> messageHandlerMethod);
And _messageHandler is defined
public void HandleMessage(QueueItem message)
{
//Do code here
}
How come at the top the messageHandler don't need a parameter for HandleMessage?
E.I
_subscriber.StartSubscribing(_messageHandler.HandleMessage(QueueItem));
Because you're not actually executing the method HandleMessage (which would happen if you had parentheses and a parameter). You are passing it as a reference to StartSubscribing which expects a method with a specified signature (void return, one parameter of type QueueItem)
Action<T> is a generic delegate, this particular version is a delegate which specifies no return (void) and a single parameter of type T (or QueueItem in your example)
In fact, it is the method StartSubscribing (or perhaps the class it belongs to) which is likely to provide the instance of QueueItem - perhaps something like this:
public override void StartSubscribing(Action<QueueItem> messageHandlerMethod)
{
// do something to get/create a QueueItem
QueueItem item = SomeMagic();
// pass it back to the passed in delegate
messageHandlerMethod(item);
}
With _subscriber.StartSubscribing(_messageHandler.HandleMessage) you are using a more direct way for _subscriber.StartSubscribing(msg => _messageHandler.HandleMessage(msg)).
So it does need a parameter.
What I want is to have a method that accepts a type as a parameter and cast a variable into that type within the method in C#
as an example I want to pass a UI element to this helper method and extract its DataContext's (which is bound dynamically at runtime) Description. I want to use this method in a more generalized manner so that I can pass in the DataContext's type also.
private String GetDescription(FrameworkElement element, Type type) {
return (element.DataContext as type).Description;
//or
//return ((type)element.DataContext).Description;
}
both the ways it ends up with a compile time error.
I tried using generics as well but it was not successful as i might not understood properly.
It would be really great if someone could explain how to do this in a simple manner.
Write a interface and implement it for your classes:
public interface IDescribable
{
string Description{get;}
}
Implement this object on your desired classes:
public class MyClass:IDescribable
{
// other members
public string Description{get; set;}
}
Then you could even write an extension method to extract the string:
public static string GetDescription(this FrameworkElement element)
{
var contextData= element.DataContext as IDescribable;
return contextData!=null
? contextData.Description
:"";
}
Or if you don't want implement interface use reflection:
private string GetDescription(FrameworkElement element)
{
var decProp= element.DataContext.GetType().GetProperty("Description");
return decProp!=null
?decProp.GetValue(element.DataContext)
:"";
}
I'm working on this project with some other colleges. It's build on Windows MDI forms and we have a custom method for loading the forms due to the specific need of the customers.
At the begining the method had to take only two parameters :
protected void LoadForm<T>(ToolStripButton formButton, string buttonText) where T : Base Form
and because even that was inherited code we inherit some forms that didn't need to pass any parameters so we had this method:
protected void LoadForm<T>()
{
LoadForm<T>(null, null);
}
Then my college needed to pass another parameter : long? EntityId and again to not break the existing code he changed the above method to :
protected void LoadForm<T>(long? loadEntityId = null) where T : BaseForm
{
LoadForm<T>(null, null, loadEntityId);
}
And the actual method with the business logic became:
protected void LoadForm<T>(ToolStripButton formButton, string buttonText, long? loadEntityId = null) where T : BaseForm
Now I need to pass fourth parameter myself and again the issue with not breaking the previous code is actual. I thought I got the logic right but it seems that I haven't. I need to pass a boolean parameter bool IsClosingForm so I made the method witht he business logic like this:
protected void LoadForm<T>(ToolStripButton formButton, string buttonText, long? loadEntityId = null, bool IsClosingForm= true) where T : BaseForm
And add new method to handle situations when the bool parameter need to be passed:
protected void LoadForm<T>(bool IsClosingForm= true) where T : BaseForm
{
LoadForm<T>(null, null, null, IsClosingForm);
}
And also modify my colleges method to call a four parameter method:
protected void LoadForm<T>(long? EntityId = null) where T : BaseForm
{
LoadForm<T>(null, null, EntityId, true);
}
But it's not working like that and the error I get is:
The call is ambiguous between the protected void LoadForm<T>(long? EntityId = null) and protected void LoadForm<T>(bool IsClosingForm= true) method
It's obvious I'm not getting the logic here so I have two questions - how to modify the code to make it work and if possible I would like someone to point what cause my mistake so I can get the logic behind this. First it seemed very easy task, but now I think I'm missing something that I should not leave not understood.
This is the ambiguous call:
LoadForm<T>()
The call can be interpreted as one of the following two methods:
LoadForm<T>(long? EntityId = null)
or as LoadForm<T>(bool IsClosingForm= true)
Since you have specified a default value for the parameters, the compiler cannot distinguish between the two overloads when the argument is not supplied.
Maybe you can remove the default values and create another overload that does not expect any argument. This new overload would be used when LoadForm is invoked without parameters
Is it possible that you are passing in 0 or 1 in and its an ambiguous as to it being a bool or long? Possibly cast it to a long in the method call to ensure getting registered as long and not bool.
I wanted to code a helper method in Unit test project, which will initialize the presenter set the views instance to it and set the presenter state.
It threw me the exception:
the type parameter cannot be used with type arguments
Code:
public static **TPresenter<TView>** Initialize<TPresenter,TView>()
where TPresenter: BasePresenter<TView>, new()
where TView : new()
{
}
After couple of minutes I found the issue was with my return type TPresenter<Tview>
I read few posts which didn't clearly explain Why I'm not be able to say T1<T2>
I was forced to make the presenter assignment through reference parameter. Any explanations are welcome!
Basically there's no way of saying that a type parameter is itself a generic type with a particular number of type parameters - which you need to be able to do in order to make TPresenter<TView> make sense.
It's not clear what you mean by making it work via a reference parameter - whatever type you used for that ref parameter should be fine as a return type too. My guess is that it was just of type TPresenter, not TPresenter<TView>.
There is no such thing as a TPresenter<TView> it is meaningless. TPresenter is just a placeholder, until it is constrained by the where it could be anything, e.g. there is no int<tview> so you can't have that. Once you add the constraint it means it has to be a BasePresenter<TView> or some derived type so it will always be a Something<TView> so again TPresenter<TView> is meaningless.
This is an old one, but I hit it too. In the Class definition, just use the single type, then multiple types where you use it. E.g:
public class Template1<T>{}
void SomeFunc()
{
<Template1<SomeClass1,SomeClass2>> someValue = new <Template1<SomeClass1,SomeClass2>>()
}
//or even:
void SomeOtherFunc<U,V>()
{
<Template1<U,V>> someValue = new <Template1<U,V>>();
}
I was getting similar error in my code. #Jon Skeet correctly points to the right direction. The return type is already generic, as specified by TPresenter : BasePresenter<TView>. So we can simply use it as TPresenter instead of TPresenter<TView>.
public class BasePresenter<T>
{
}
public class Demo
{
public static TPresenter Initialize<TPresenter, TView>() where TPresenter: BasePresenter<TView>, new()
{
return null;
}
}
Small addition, I came here because i was trying to write an extension method;
public static T AutoJoinGroup<T, TD>(this T<TD> groupHubClientBase, string groupName)
where T : GroupHubClientBase<TD>
{
...
}
As you can see, I tried to use T<TD> which is incorrect, you can just use T
I have the following C# class:
public class MyType<T>
{
public void TryParse(string p_value)
{
T value ;
Parser.TryParse(p_value, out value);
// Do something with value
}
}
The point is to call the right Parser.TryParse method, depending on the generic type T.
This uses the following static class:
static public class Parser
{
static public void TryParse(string p_intput, out object p_output)
{
// Do something and return the right value
}
static public void TryParse(string p_intput, out double p_output)
{
// Do something and return the right value
}
static public void TryParse(string p_intput, out int p_output)
{
// Do something and return the right value
}
}
I expected this to work: In the worst case, the "object" TryParse would be called. Instead, I have two compilation errors:
CS1502: The best overloaded method match for 'Parser.TryParse(string, out object)' has some invalid arguments
CS1503: Argument 2: cannot convert from 'out T' to 'out object'
Question 1: I don't understand why this doesn't work: I can be naive, but aren't all C# objects supposed to derive from "object" ? Why T cannot be converted to object?
Question 2: How can I dispatch a method with generic type T into the right non-generic methods (i.e. MyType<T>.TryParse calling the right Parser.TryParse according to the right type of T) ?
Note
The question was edited to reflect the original question intent (as written in the title: How to dispatch C# generic method call into specialized method calls)
Actually, ref and out parameters do not allow type variation. So, to pass a variable to a method expecting an out object parameter, that variable must be declared as object.
From the specification (§10.6.1.2 and §10.6.1.3)
When a formal parameter is a reference parameter, the corresponding argument in a method invocation must consist of the keyword ref followed by a variable-reference (§5.3.3) of the same type as the formal parameter.
When a formal parameter is an output parameter, the corresponding argument in a method invocation must consist of the keyword out followed by a variable-reference (§5.3.3) of the same type as the formal parameter.
See: Why do ref and out parameters not allow type variation? for some insight into why.
Bonus question: How can I dispatch a method with generic type T into the right non-generic methods (i.e. MyType<T>.TryParse calling the right Parser.TryParse according to the right type of T)?
I'm going to turn it back around on you. Why are you doing this? If you are invoking MyType<T>.TryParse as, say, MyType<int>.TryParse, why not call Int32.TryParse directly? What is this extra layer buying you?
I know this is somewhat low-tech, but I have had the same problem, where I solved it by making a Dictionary<Type, Parser> containing the individual parsers. I will be interested in what answers this questions bring.
Regards,
Morten
Current solution
The current solution I use at work is based on dynamic dispatch, that is, the keyword dynamic as defined on C# 4.0.
The code is something like (from memory) :
public class Parser
{
static public void TryParse<T>(string p_input, out T p_output)
{
// Because m_p is dynamic, the function to be called will
// be resolved at runtime, after T is known...
m_p.DoTryParse(p_input, out p_output) ;
}
// The dynamic keyword means every function called through
// m_p will be resolved at runtime, at the moment of the call
private dynamic m_p = new Parser() ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private void DoTryParse(string p_input, out double p_output)
{ /* Do something and return the right value */ }
private void DoTryParse(string p_input, out int p_output)
{ /* Do something and return the right value */ }
// etc.
private void DoTryParse<T>(string p_input, out T p_output)
{
// fallback method... There are no dedicated method for T,
// so p_output becomes the default value for T
p_output = default(T) ;
}
}
The elegant part is that it can't fail (the fallback function will be called, if none with a better signature match is found), and that it follows a simple pattern (overload the function).
Of course, the real-life, production code is somewhat different, and more complicated because, with but one public static method, I want to :
parse both reference objects (classes) and value objects (structs)
parse enums
parse nullable types
I want to offer the user the possibility to derive from Parser to offer its own overloads in addition to the default ones
But I guess the use of dynamic in the current solution is, in the end, the same thing as doing reflection as done in the original answer below. Only the "notation" changes.
Conclusion, I now have the following method :
public class Parser
{
static public void TryParse<T>(string p_input, out T p_output)
{
// etc.
}
}
which is able to parse anything, including in situations where T is not known at compile time (because the code is generic).
Original answer
Jason's answer was right about the first question (about the compiler errors). Still, I had no solution to my problem (dispatching from a generic method to non-generic methods according to the runtime generic type T).
I tried LukeH's answer, but it didn't work: The generic method is always called, no matter what (even when removing the out qualifier of the second parameter).
Morten's answer is the most sane one that should works, but it doesn't make use of reflection.
So, to solve my own problem, I used reflection. This needs the rewriting of the generic TryParse method:
public class MyType<T>
{
public void TryParse(string p_value)
{
T value = default(T);
// search for the method using reflection
System.Reflection.MethodInfo methodInfo = typeof(Parser).GetMethod
(
"TryParse",
new System.Type[] { typeof(string), typeof(T).MakeByRefType() }
);
if (methodInfo != null)
{
// the method does exist, so we can now call it
var parameters = new object[] { p_value, value };
methodInfo.Invoke(null, parameters);
value = (T)parameters[1];
}
else
{
// The method does not exist. Handle that case
}
}
}
I have the source code available if needed.
This problem intrigued me, so I did some research and found a nice thing by Paul Madox. This seems to do the trick.
public static T SafeParseAndAssign<T>(string val) where T: new()
{
try
{
T ValOut = new T();
MethodInfo MI = ValOut.GetType().
GetMethod("Parse", new Type[] { val.GetType() });
return (T)MI.Invoke(ValOut, new object[] { val });
}
catch
{
// swallow exception
}
return default(T);
}