views:

142

answers:

5
+3  Q: 

combining methods

I am a beginner so please forgive my ignorance.

In my app I have a lot of copy and paste code that is exactly the same and performs exactly the same function (button click events and the like). These redundant code live in the code-behind of many of my pages. So I decided to reduce the code duplication and to move these methods into a class file and only make a call to them from the code-behind pages.

Here is an example of a button click event in my code behind calling the methods from a class file:

#region DELETE selected users - button

protected void btnDeleteSelected_Click(object sender, EventArgs e)
{
    try
    {
        UserGvUtil.DeleteSelectedUsersAndProfiles(GridView1, Msg);
    }
    catch (Exception ex)
    {
        UserGvUtil.ExceptionErrorMessage(Msg, ex);
    }
    finally
    {
        UserGvUtil.RefreshGridView(GridView1);
    }
}

#endregion

My question is, can I combine this try/catch block into yet another method and move it to the same class file? So, the only thing I have in the click event is a single line of code.

Does it make sense to do this? Not sure why, but I would like to have my code behind files as clean and simple as possible so I can make all the edits in a single place.

Sorry if I make no sense. I'm just learning about classes and methods and it floods my head with lots of ideas.

+3  A: 

You can move the stuff inside the try block into an anonymous delegate that you pass to a shared method that has a try/catch. You really don't need to put the refresh into the finally, though. In fact, I would think you would only want to run it if the try block succeeds.

Steven Sudit
+1: you're the first to understand the OP's problem and suggest a real solution. Not sure if I would actually implement something like this just to save a few lines of code, but it *does* work for the OP's case.
Juliet
@Juliet: I agree that saving a few lines isn't reason enough to add possible complications, but what kekekela said about DRY is entirely relevant. If it is *not* coincidental that all of the event handlers have a try block that logs exceptions in a particular way and also refresh the grid, then this means copying and pasting is inappropriate. Putting the meat in a delegate that gets passed to a single helper method would make the code more maintainable.
Steven Sudit
"+1: you're the first to understand the OP's problem" -- No, he's really not.
kekekela
@kekekela: With all due respect, only factoring out the try/catch block follows DRY. Having each event handler call a method that copies and pastes that block is a net loss in maintainability.
Steven Sudit
@steven Sudit, Does that mean I should move the UserGvUtil.RefreshGridView(GridView1); into the try block and delete the finally?
Scott W
@Steven: you keep making the assumption that there is a 1:1 ratio between blocks of code being moved out of handlers and methods created whereas the question seems to make it clear that this isn't the case.
kekekela
@Scott: Yes, I think so. Even if I'm wrong and you really, really want to refresh the grid after the worker method fails, it still shouldn't go into the `finally` block. Instead, it could go after and outside of the `catch`.
Steven Sudit
@kekekela: The question makes clear that the goal is to remove the redundancy caused by having the same try/catch in each event handler. Your downvote is unconvincing.
Steven Sudit
@Steven Haha, how many times are you going to accuse me of downvoting? Believe me, I'd tell you if it was me.
kekekela
@kekekela: Well, if it's not you then I hope the real downvoter will pipe in and explain their basis, if any.
Steven Sudit
A: 

I have a lot of copy and paste code that is exactly the same ... (button click events and the like)

Just move all of that code that's duplicated behind multiple click handlers into a method in a separate class and pass whatever's needed (in this case the GridView and whatever that MSG object is) as parameters. If its saving significant duplication then it would make sense to do so. DRY (Don't Repeat Yourself) is a valid principle.

kekekela
The duplication is in the error-handling that each of these methods calls. Moving everything into yet another method would not improve the code.
Steven Sudit
If you're repeating the same try/catch/finally in multiple handlers as he clearly stated that he was, then yes, by definition, it would.
kekekela
Again, having `btnDeleteSelected_Click` call `DeletedSelectedClick_Helper`, just moves code around without removing redundancy. Worse, it doubles the number of methods. This is not helpful.
Steven Sudit
@Steven Again, read his post. He said he had the same code behind multiple button clicks, its not just going to be called from a single btnDeleteSelected_Click. I'm not talking about moving code that will be called from one handler into another method, I'm talking abou code being moved into a separate class and called from multiple btnWhatever_Clicks on multiple pages.
kekekela
Heh, downvoting against your own strawman I take it, that's cute.
kekekela
If you correct your error, I will remove the downvote. Thank you for the retaliatory downvote, though.
Steven Sudit
@Steven a - I didn't downvote you, rep was 1900 when you started this and is 1908 after your downvote + an upvoteb - I think I've explained that there's not an error here but whatever, the guy has plainly stated that he has the exact same code behing multiple button-clicks. If keeping a downvote in place based on your misinterpretation gives you some kind of SO power-high, go for it.
kekekela
If it's a misinterpretation, then all you have to do is correct it.
Steven Sudit
@Steven Ok, thanks for your comments. Good job.
kekekela
I can only take this to mean that I am not misinterpreting anything here.
Steven Sudit
@Steven Yeah, no misinterpretations here, please move along.
kekekela
You know, I'm actually trying to correctly interpret what you said, and you're doing nothing to help me. That's your choice, but don't expect me to thank you for it, much less reward you.
Steven Sudit
@Steven Uh, I'm not asking for any thanks or a reward (wtf?), let your downvote stand by all means, its not that big of a deal. If you honestly don't understand what's I've said then just keep moving. You've already made your opinion known via vote and comments, I'm not sure what you're trying to accomplish here.
kekekela
I'm not interested in downvotes or upvotes -- this isn't a video game for me. My goal is to understand and learn. So far, all I've learned is that I don't understand your view. Again, I cannot compel you to explain yourself, but that doesn't mean I'm happy about it.
Steven Sudit
"I'm not interested in downvotes or upvotes -- this isn't a video game for me. My goal is to understand and learn. " haha ok
kekekela
http://en.wikipedia.org/wiki/Passive%E2%80%93aggressive_behavior
Steven Sudit
I saw your hostile response before you deleted it. You were right to delete it, though. In fact, you'd do well to delete this entire answer.
Steven Sudit
@Steven Three times in the comments I've explained my position and you just come back with non-sequiturs like "correct it" and "correct your error, I will remove my downvote" before veering off into rants about how you aren't going to reward or thank me and votes don't matter to you and I'm casting "retaliatory" down-votes etc, etc, etc. I'm not being passive aggressive, I just have no desire to continue a conversation with you.
kekekela
@Steven "I saw your hostile response before you deleted it." -- Had to delete because my edit went over the timeline, don't worry, its back. " You were right to delete it, though. In fact, you'd do well to delete this entire answer." -- Ok thanks man, I truly value your advice and eagerly await your further creepy trolling of this comments section.
kekekela
Four of my answers to other questions were downvoted within a few seconds of each other just now. Thanks for the creepy trolling.
Steven Sudit
@Steven Wow dude, you're a freak.
kekekela
And you're an obvious liar.
Steven Sudit
@Steven hahaha, ok
kekekela
@Steven @kekekela Do we have to put you guys in timeout? Chill. Also, keke **is** lying, lol.
Will
A: 

You could maybe start by creating a class in a new file and using it in your pages. I think it would be a good start, so that later on you can create more complicated classes and re use all your code.

public class Additionner //declaring the class
{
   public int calcul(int a, int b) //declaring a method that add 2 numbers
   {
     return a + b; //returning result
   }
}

Then use your class inside one of your page

Additionner MyCoolObject = new Additionner(); //create the Additionner
int Result = MyCoolObject.calcul(1,2); //call the "calcul" method
Lobsterm
I fail to see the relevance.
Steven Sudit
The code is self-explanatory, the comments are noise. If I saw comments like `create the object` and `call the method` in production, they'd be deleted with prejudice and the original developer encased in carbonite.
Juliet
@Juliet: Agreed that the comments are useless, but as much as I can see what the code does, I don't see what that has to do with the question.
Steven Sudit
I was explaining how to use class outside his regular files to avoid repeating code. So he can create object that he will reuse everywhere in his project. It is a deadly simple example and obviously you would look like a noob if you put this in a production project. I don't see the relevance of all the comments on my post.
Lobsterm
He already knows how to use a class: look at the calls to `UserGvUtil.DeleteSelectedUsersAndProfiles`. He explicitly asked about factoring out the try/catch block.
Steven Sudit
My bad then, thanks Steven Sudit :)
Lobsterm
If you'd like to modify your answer, I'd be glad to remove the downvote.
Steven Sudit
+1  A: 

You can wire the event handlers manually.

btnDeleteSelected1.Click += Events.BtnDeleteSelected_Click;
btnDeleteSelected2.Click += Events.BtnDeleteSelected_Click;
...
btnDeleteSelected3.Click += Events.BtnDeleteSelected_Click;

public static class Events
{
  public static BtnDeleteSelected_Click(object sender, EventArgs e)
  {
     ...
  }
}

Edit (for downvoters: ???) The code will give you a one liner and you won't have to worry about writing custom events when they are all the same.

Also, if the utility methods have the same signature you could have a generic method:

public void ExecuteGvMethod(Action<GridView, string> gvMethod, GridView gv, string msg)
{
    try
    {
        gvMethod(gv, msg);
    }
    catch (Exception ex)
    {
        UserGvUtil.ExceptionErrorMessage(msg, ex);
    }
    finally
    {
        UserGvUtil.RefreshGridView(GridView1);
    }
}

And in code:

public static class Events
{
  public static BtnDeleteSelected_Click(object sender, EventArgs e)
  {
    ExecuteGvMethod(UserGvUtil.DeleteSelectedUsersAndProfiles, (GridView)sender, "hi of whatever");
  }
}
Jaroslav Jandek
-1: a single class to wireup all event handlers? This is NOT the sort of design pattern we'd want to be teaching C# beginners.
Juliet
Again, how would this help?
Steven Sudit
@Juliet: It is obviously specific to GridView. And yes, if the handlers all perform the **exactly same functionality**, don't you think the method used should be the same?@Steven Sudit/Juliet: What I understood is that he is using the code in many places and does not want to replicate the code.
Jaroslav Jandek
The helper method that takes a delegate is an improvement, but the specifics are not good. First, there's no reason not to just take any delegate, even one with a different signature. Second, you repeated the OP's error regarding `finally`. I'm upvoting you for the delegate part only.
Steven Sudit
@Steven Sudit: I just copy-pasted his code. Also if he threw exceptions or whatever in the catch code, the `finally` would be useful. I would leave it to the programmer. It seems that it is unnecessary in this example. If you take any delegate, you need to cast them to the correct type or reflect them (ugly) so you need to know, specify or detect (`is`) their type, which is not really straightforward and leads to more code and errors - or I have somehow mistunderstood your point.
Jaroslav Jandek
The `finally` is useless regardless, because the `catch` does not rethrow. This means that *any* code after the `catch` will get executed unless the try block contains a `return`, which it does not. The `catch` code just logs, so it's not going to throw any exceptions.
Steven Sudit
The delegate just needs to be executed inside a try block. It already contains whatever parameters it uses as closures, so you don't really care what its type is, but the type is `Action`, which is to say no return, no parameters. There is no need for downcasting. Does that help explain what I was trying to say?
Steven Sudit
@Steven Sudit: In that case ur definitely right, a closure would work - I still prefer explicit signature where convenient, though. Thanks for explaining.
Jaroslav Jandek
No, you may be right. For example, we *may* actually want to have a delegate that takes parameters, such as a `Control` subtype instance and a string with a message. We could then use the message in the `catch` block, while passing both in to the delegate. It's certainly a plausible alternative, but I don't happen to see any strong motivation for it at the moment, at least not with the small sample we've seen.
Steven Sudit
A: 

I'm risking a downvote, but here are my 2 cents:

Instead of using a try/catch, use a method that returns a status code. For instance (just an idea, instead of using enum you can use a more complex class):

public enum StatusCode
{
    Success = 1,
    Error =2
}

public class UserGvUtil
{
    public StatusCode getStatusAfterDelete(GridView GridView1, string Msg) 
    {
        try
        {
            DeleteSelectedUsersAndProfiles(GridView1, Msg);
            Return StatusCode.Success;
        }
        catch (Exception ex)
        {
            UserGvUtil.ExceptionErrorMessage(Msg, ex);
            Return StatusCode.Error;
        }
    }

//your other methods here
}

Then in code behind:

protected void btnDeleteSelected_Click(object sender, EventArgs e)
{
    StatusCode sc = UserGvUtil.getStatusAfterDelete(GridView1, Msg);

    //then do something with the status code if you have to:
    if (sc==StatusCode.Error) throw new Exception("Error deleting users and profiles");
    else UserGvUtil.RefreshGridView(GridView1);

}

That way you can change your try/catch later if you think it affects performance etc...
Hope it helps.

Francisco
I'm not going to downvote you right now, but I will explain why I don't believe this is a good answer. First, there's no good reason for `getStatusAfterDelete` to return a special enum instead of a simple `bool`. Second, that method name violates .NET conventions due to the leading lower case. Third, and much more importantly, there is no point to the method. All you've done is move code further back, without removing redundancy. Worse, you added redundancy by throwing an unnecessary exception, and you didn't even put the refresh into the helper. This change would be a net loss.
Steven Sudit
There is a good reason. His code was just an example, but what if he wants to handle different types of errors later? Second, conventions are not relevant in my answer, because it's just an example. He won't copy-paste my code. Third, ok I didn't put the refresh, my bad. Again, it's just an example, it's just a way to show he can handle the error any way he wants after he gets the status code returned. I don't see the need to start throwing exceptions anyway...
Francisco
I agree that there's no need to throw exceptions here. In fact, the code catches and logs exceptions without rethrowing them, and that's entirely reasonable. I don't see how this explains the enum, and I can't agree that conventions are irrelevant. I could have also added that you're not supposed to ever throw a base `Exception`, but as the whole line needs to be removed, I did not see a reason to at the time.
Steven Sudit