tags:

views:

170

answers:

6

I have 2 methods which have almost the same content:

public string Method1(int someInt)
{
    if(someBoolean)
        return "test";

    if(someOtherBoolean)
    {
        return "dfjakdsad";
    }
    else
    {
        string myStr = getString(someInt)
    }
}

 public string Method2(myEnum myenum)
 {
    if(someBoolean)
        return "test";

    if(someOtherBoolean)
    {
        return "dfjakdsad";
    }
    else
    {
        string myStr = getString(myenum)
    }
 }

The differences are the method signatures and the single statement in the else, string myStr = getString

Method1 is called from many places so it has to stay in some way. How would I refactor that?

+5  A: 

If your enum can cast to an int, and assuming your getString returns the numeric value of the enum and not the text, then simply:

public string Method2(myEnum myenum) 
 { 
    return Method1((int)myenum);
 } 

As an interesting aside - I've seen this situation coined "same whitespace, different values".

Also as an interesting aside - my head-based compiler says that code won't actually compile :-)

I wouldn't worry about refactoring this - the gain is slightly better code readability and possibly, if changes were to occur, decreased complexity on a change - however it's not complicated code so the additional testing overhead if it's used a lot might outweight the gain.

Update: if the enum getString returns the text, ie "Foo", then you cannot refactor this code in the manner I've described.

Adam
@Adam, in C#, afaik all enums are integers
Chad
Your head-based compiler is entirely correct.
Adam Robinson
@Chad, by default, yes. Although you can specify what underlying type is used.
Charlie Salts
@Chad: No. By default, an `enum` is an integer, but you can define an `enum` as any integral type (`byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`).
Adam Robinson
Does this really answer the question? If getString(myEnum) return "MyEnumValue" this is not equivalent. It answers the question if getString(myEnum) returns a string representation of the int value of the enum.
btlog
@Adam apologies. Amazing how code draws my eye away. Seems like a large assumption.
btlog
@btlog indeed - if such is the case and your scenario is correct, then the refactoring cannot occur - I'll amend my answer.
Adam
+5  A: 

I would recommend using a generic:

public string Method<T>(T arg)
{
    if(someBoolen)
        return "test";

    if(someOtherBoolean)
    {
        return "dfjakdsad";
    }
    else
    {
        string myStr = getString(arg)
    }
}

This assumes that getString is itself generic or can handle any type of object.

JSBangs
That seems like a heavy assumption...
Adam Robinson
Generics might be a little overkill - plus the change would need applying to all callers and the type would need to transfer through to getString. However if getString is currently accepting objects, one benefit of generics is the removal of boxing.
Adam
@Adam, callers in the same assembly shouldn't need to be changed at all, as the compiler can infer types at the call site. The only difficulty would be for callers from outside assemblies that can't be recompiled, but you can fix that with a simple wrapper.
JSBangs
@JSBangs I thought the type can only be inferred in the latest version of C#?
Adam
@Adam, type inference for generic function arguments has existed for as long as there have been generics. You can call `Method<T>(T arg)` as `Method(stringObject)`, and the compiler will figure out that you mean `Method<string>(stringObject)`.
JSBangs
Nice, didn't know that - well, to be honest, never tried lol
Adam
A: 

Would casting the enum and calling down to the int version of the function work in your scenario?

public string Method(myEnum myenum)
{
    return Method((int)myenum);
}

public string Method(int someInt) {
    if(someBoolen)
        return "test";

    if(someOtherBoolean)
    {
        return "dfjakdsad";
    }
    else
    {
        string myStr = getString(someInt)
    }
}
qstarin
If casting doesn't work, you can simply write the function that gives you the appropriate int for the given enum, then call it:`return Method(getAppropriateInt(myenum));`
Carl Manaster
+4  A: 

You could pass a Func as the second parameter

public string Method(Func<string> myFunc)
{
    if(someBoolen)
        return "test";

    if(someOtherBoolean)
    {
        return "dfjakdsad";
    }
    else
    {
        string myStr = myFunc();
    }
}

Method(myEnum => getString(myEnum));
Method(someInt => getString(someInt));

The generic is the best solution if you are always calling getString as answered by JSBangs.

btlog
+3  A: 

Is the code in question working, tested code? If so, don't change it without a good reason. I would ask myself two questions:

1) Is making your code a few lines shorter and slightly less redundant really worth the risk of accidentally introducing a bug?

2) Is this the place in the code where my limited time, effort and skill can be best applied?

If this is the worst problem in your code then congratulations, you have awesome code. I would be looking somewhere else for bigger fish to fry.

Eric Lippert
But don't you think this code violates DRY and would cause maintenance night-mare ? hence be re-factored.
this. __curious_geek
@this. __curious_geek - not all violations of DRY incur nightmares :-) I would argue this violation in return for no testing overhead is OK enough.
Adam
@Eric, just to clarify, it's your time and effort that are limited, not your skill, right...? :-)
gkrogers
@gkrogers: I don't know about you, but my skill is definitely limited.
Eric Lippert
@this. __curious_geek: Your question is answered by my point #1. If the cost of making correct code less redundant is worth the risk of accidentally making it incorrect then consider doing so. But to not ask the question in the first place is reckless.
Eric Lippert
A: 

I would propose the inverse of Adam's answer:

Enums are strongly-typed, whereas int's are not--so you should promote using the enum whenever possible. Therefore I would have the "real" method require an enum as the parameter, and an overload which accepts an int and casts it to the enum.

Note that both methods share the same name, and the enum one is placed first: this means intellisense will "suggest" the enum method first but show the int as an available option:

public string Method1(myEnum myenum)
{
    if(someBoolen)
        return "test";

    if(someOtherBoolean)
    {
        return "dfjakdsad";
    }
    else
    {
        string myStr = getString(myenum)
    }
 }

public string Method1(int someInt)
{
    return Method1((myEnum) someInt);
}

Update: As Adam asks, this solution does carry the implication that you always will pass an int which corresponds to a member of your enum. In my experience I this is the common practice (if the enum only maps to a subset of valid ints, then what value does the enum provide?)--however it's worth making sure it fits your use-case.

STW
Would this mean that all int's given would need to be able to cast to the enum? ie, the enum to have the values present.
Adam