Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I chanced upon this code written by the Solution Architect for an MS CRM project and I am lost for words. Am I going crazy or is this code OK?

string returnedOptionSetStringValue=string.Empty;
int returnedInt = 0;

Utils.RetrieveOptionSetLabelOrValue(CRMAccess.xrmService, Contact.EntityLogicalName, "new_status", optionSetValue.Value, string.Empty, ref returnedOptionSetStringValue, ref returnedInt, CRMAccess.tracerService);

The method within the Utils class is as follows

public static void RetrieveOptionSetLabelOrValue(IOrganizationService CrmWebService, string EntityName, string AttributeName, int OptionSetValue, string optionSetText, ref string returnedText, ref int returnedNumber, ITracingService tracerService)
{
    string returnLabel = string.Empty;
    tracerService.Trace("starting in function ");
    OptionMetadataCollection optionsSetLabels = null;

    tracerService.Trace("in retrieve option set label with values:" + OptionSetValue + " and text " + optionSetText);

    optionsSetLabels = RetrieveOptionSetMetaDataCollection(ref CrmWebService, EntityName, AttributeName);


    foreach (OptionMetadata optionMetdaData in optionsSetLabels)
    {
        tracerService.Trace("now in loop with  " + optionMetdaData.Label.UserLocalizedLabel.Label + " and " + optionMetdaData.Value.Value);
        //we have number we need text from optionset
        if (OptionSetValue != 0)
        {
            if (optionMetdaData.Value == OptionSetValue)
            {
                returnedText = optionMetdaData.Label.UserLocalizedLabel.Label;
                break;
            }
        }
        //we have text we need number from optionset
        else if (optionSetText != String.Empty)
        {

            if (optionMetdaData.Label.UserLocalizedLabel.Label == optionSetText)
            {
                returnedNumber = optionMetdaData.Value.Value;
                break;
            }

        }
    }
}
share|improve this question

closed as off-topic by 200_success Jun 29 '15 at 14:47

This question appears to be off-topic. The users who voted to close gave this specific reason:

If this question can be reworded to fit the rules in the help center, please edit the question.

    
I wouldn't say it's bad practice or bad code exactly - it's just not idiomatic for C#. This kind of code would be very familiar for someone who came from a C++/COM/Win32 background. – MattDavey Apr 30 '13 at 9:57
    
@MattDavey But, why would someone use ref for a method like this. Honestly, I have not seen a proper real use for ref. – Kanini Apr 30 '13 at 10:01
1  
If you didn't write this code, then I think this question is off-topic here, per the FAQ. – svick Apr 30 '13 at 12:22
    
@svick Strictly per the rules, I agree that this may be off-topic here, but the question contains code and it is actual code from a project rather than pseudo-code or example code and I want the code to be good code and to the best of my knowledge the code works and I want feedback about any / all facets of the code. So, 5 out of 6 questions are answered with an Yes. There are similar questions in this site for instance - codereview.stackexchange.com/questions/23134/… and plenty others. Personally, I do not think it is off-topic. – Kanini Apr 30 '13 at 12:38
    
@Kanini check out System.Int32.TryParse for a "proper real" use for a ref parameter. There are many reasons to pass a parameter by reference. But like I said, this is not considered idiomatic in C#, and to someone who only knows C# it may appear strange or wrong. – MattDavey Apr 30 '13 at 13:56
up vote 4 down vote accepted

To answer your question, to me this is an illegitimate use of ref on a value type parameter.

Here's why:

1) Legit usages of ref are when you need to keep track the value of ref parameter or when passing the value is expensive. e.g. when using recursion to traverse a tree and you need to keep track of its depth.

Node<T> FindNode(T root, int nodeValue, ref int depth);

2) The code by the Solution Architect looks confusing and weird because it should be using out parameters instead (not to mention that it smells and looks like it deserves some refactoring).

Unlike ref params, out params need not be initialized prior being passed; which is why returnedOptionSetStringValue and returnedInt are being initialized.

For further reading: http://msdn.microsoft.com/en-us/library/t3c3bfhx(v=vs.80).aspx http://stackoverflow.com/questions/1516876/when-to-use-ref-vs-out

share|improve this answer

This is done when you want to catch any modifications inside the method you'r calling.

returnedInt is a value type, so he pass it as a reference to ensure the above. returnedOptionSetStringValue is a reference type but the reference to the string is passed by value, so, the same.

Is it a good practice? I don't think so, especially since the method is void. Instead you could return the string and pass the int as an out param.

share|improve this answer
    
Thanks! Yes, I understand why the ref and passing it as a reference. But is it not better to have two separate methods one returning a string (pass the int) and the other returning an int (pass the string)? Of course, having one method with void and your suggestion will ensure that there is only one method and is better. +1 for that! – Kanini Apr 30 '13 at 8:04
    
OK then. I didn't suggest you should keep the void method, au contraire, you should return something, since you are already implicitly returning two values. In the other hand, this type of practice is pretty common in C++ as svick said, you shouldn't need to look to much before you find a boolean method "returning" someting else, so you can catch it if true. So theorically one method should do one thing, but in practice sometimes is "better" this kind of thing. Just do it right. And for me, right is what I answer above. – Enrique Medina Apr 30 '13 at 12:58
    
Ah, I see! Sorry, I could not quite understand what you meant by "you shouldn't need to look to much before you find a boolean method "returning" something else so you acn catch it if true". Care to elaborate? – Kanini Apr 30 '13 at 14:09
    
what I mean is that is pretty common to find something like bool Func(param) and in other method ask if(Func(param))/*do something with param*/ – Enrique Medina Apr 30 '13 at 16:15

Methods should only do one thing - in your example I think you should have seperate methods: GetOptionSetLabel and GetOptionSetValue.

I'm always wary of public static void methods as well. I would expect something more like this (tracing code ommitted):

public class YourSensibleClassName
{
    private IOrganizationService organizationService;

    // Constructor injection with a DI framework?
    public YourSensibleClassName(IOrganizationService service)
    {
        this.organizationService = service;
    }    

    public int GetOptionSetValue(
        string entityName, 
        string attributeName, 
        string optionSetText, 
        ITracingService tracerService)
    {
        var optionSetLabels = GetOptionSetLabels(entityName, attributeName);

        var result = optionSetLabels
            .FirstOrDefault(
                label => label.Label.UserLocalizedLabel.Label.Equals(optionSetText));

        if (result == null) 
        {
            throw new Exception(
                string.Format(
                    "No value exists for the specifed optionSetText {0}", 
                    optionSetText));
        }

        return result;
    }

    public string GetOptionSetText(
        string entityName, 
        string attributeName, 
        int optionSetValue, 
        ITracingService tracerService)
    {
        // Code to get the text from the value
    }

    private OptionMetadataCollection GetOptionSetLabels(
        string entityName, 
        string attributeName)
    {
        // Code to get the OptionMetadataCollection and this method needs refactoring too:
        // RetrieveOptionSetMetaDataCollection(ref this.organisationService, entityName, attributeName);
    }
}

I'd expect this to be called like:

YourSensibleClassName c = new YourSensibleClassName(CRMAccess.xrmService);

var optionSetValue = c.GetOptionSetValue(
    Contact.EntityLogicalName, 
    "new_status", 
    "the option set text", 
    CRMAccess.tracerService);

Judging by similar code I've seen written by people, I would think that your Solution Architect comes from a VB6 and before background and hasn't quite made the transition to C#. I'd also be worried by the lack of consistency with parameter names (both camel and Pascal case used), not using built in methods (e.g. using == string.Empty instead of string.IsNullOrEmpty(string)), using a foreach loop when Linq offers a much shorter and clearer way of achieving the goal (although internally doing roughly the same thing) and a clear obsession with ref parameters.

Just to add, one particular dev who coded like this created a method which required over 20 arrays passed by reference... It still makes me shudder.

share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.