Resolved need help returning variables from method using tuple

C# learner

Active member
Joined
Dec 22, 2022
Messages
40
Programming Experience
Beginner
This method when traced will parse out line and return the values in line to separate variables. When traced to the return everything is working fine. When passed back to the calling method the separate variables are null????

This is the method:
C#:
static (string RecordIndex, string tag, string IndividualInfo) LineBreakDown(string line, string RecordIndex, string tag, string IndividualInfo)               

                {
                   
                    // using the split method parse line into lineParts array
                    string[] lineParts = line.Split(' ');
                    // Get the total number of elements
                    int TotalElements = lineParts.Count();
                   
                    // if lineParts is not equal to null
                    if (lineParts != null)
                    {
                        // In each of the file records there are three space delimited fields. The information is used to parse into the method fields.
                        // no record in the file contains just one field
                        // The individual info needs to be combined into one field using the string.Join() method

                        switch (TotalElements)
                        {
                            // if the total elements is 2 then parse RecordIndex and tag then move blanks to IndividualInfo field and return control to calling method
                            case 2:
                                RecordIndex = lineParts[0];
                                 tag = lineParts[1];                               
                                 IndividualInfo = "";
                                return (RecordIndex, tag, IndividualInfo);
                               
                            //break;
                            // if the total elements is 3 then parse RecordIndex, tag, and IndividualInfo fields and return control to calling method
                            case 3:
                                RecordIndex = lineParts[0];
                                tag = lineParts[1];
                                IndividualInfo = lineParts[2];
                                return (RecordIndex, tag, IndividualInfo);
                            // if the total elements is greater than 3 then parse the RecordIndex and tag and combine all Individual Info to IndividualInfo 
                            default:
                                //ReadLine();
                                RecordIndex = lineParts[0];
                                tag = lineParts[1];
                                int ArrayCount = TotalElements - 2;
                                IndividualInfo = string.Join(" ", lineParts, 2, ArrayCount);
                                return (RecordIndex, tag, IndividualInfo);


                        }
                    }
                    // any problems with method
                    throw new NotImplementedException();
This is the calling method:
C#:
LineBreakDown (line = "", RecordIndex = "", tag = "", IndividualInfo = "");
What am I doing wrong?
 
Last edited by a moderator:
Moving to C# general questions...
 
Looks like some one else moved it for me...
 
The issue is that you are not doing anything with the return value of your method. You are just currently calling your method and passing in parameters.
 
The issue is that you are not doing anything with the return value of your method. You are just currently calling your method and passing in parameters.
Thank you for the prompt response. Do you have any suggestions how to setup the calling method to get the return results?
 
A lot of people seem to abandon logic when they start programming, even though programming is very logical. You don't need to ask us how to get the return value of a method when you already know how to do that because you're already doing it elsewhere with other methods, e.g.
C#:
int TotalElements = lineParts.Count();
It's clearly not a mystery how to get and use the return value of that Count method so why try to complicate the same thing for another method?
 
I guess I'm not making myself clear, sorry. I can manipulate "internal" methods ok. Defining my own method is the challenge.

I am working with a GEDCOM file. A sample record would look like this

0 HEAD Version 1.0

It is a space delimited file. The field line would that value. The method would process the line field and separate it into fields RecordIndex, tag, IndividualInfo. This part is working. When I try to return to calling method it returns null values. Should I use a Tuple? Any help would be appreciated.
 
You have a couple of choices of how you want to do things. Pseudocode below.

You could continue to use a tuple:
Use tuple return values:
static (string recordIndex,
        string tag,
        string individualInfo)
    LineBreakDown(string line)
{
    :
    case 2: return (lineParts[0], lineParts[1], "");
    case 3: return (lineParts[0], lineParts[1], lineParts[2]);
    :
}

:

var (recordIndex, tag, indivindualInfo) = LineBreakDown(line);

Or you are using an older version of C# that didn't have the tuple support go old school and use reference parameters
C#:
static void LineBreakDown(string line,
                          out string recordIndex,
                          out string tag,
                          out string individualInfo)
{
    :
    case 2:
        recordIndex = lineParts[0];
        tag = lineParts[1];
        individualInfo = "";
        return;

    case 3:
        recordIndex = lineParts[0];
        tag = lineParts[1];
        individualInfo = lineParts[2];
        return;
    :
}

:

LineBreakDown(line, out string recordIndex, out string tag, out string individualInfo);
 
Correction to my post above. out parameters, rather than ref parameters. You could also use ref parameters, but I think it may make debugging and keeping a mental picture of what is happening a little harder considering that you have 3 pieces of information that should stay together.

Speaking of which, if those 3 pieces of information should always stay together, it may make more sense to hold them all in a struct:
C#:
struct ParsedLine
{
    public string RecordIndex { get; private set; }
    public string Tag { get; private set; }
    public string IndividualInfo { get; private set; }
    
    private ParsedLine(string recordIndex, string tag, string individualInfo)
    {
        RecordIndex = recordIndex;
        Tag = tag;
        IndividualInfo = individualInfo;
    }
    
    public static ParsedLine Parse(string line)
    {
        :
        return new ParsedLine(recordIndex, tag, individualInfo);
    }
}
:
var parsedLine = ParsedLine.Parse(line);
 
Again Thank you SkyDiver. I found what I was doing wrong. I was initializing the variables in the calling method to null when they should be " ". When variables were returned they ended up null. Putting the " " in solved the problem.
 
Glad you solved your problem.

Also as a quick aside: In C# there is a distinction between a null string vs an empty string.
C#:
string a = "";                  // this an empty string
Console.WriteLine(a.Length);    // prints out 0.

string b = null;                // this is a null string
Console.WriteLine(b.Length);    // this will throw a null reference exception
 
I'd use a class rather than a struct there. I also use PascalCase for named tuple members, because they're like public properties

Don't forget about records if your C# is modern enough to have them

I aim to avoid out/ref unless I'm writing some similar function to int.TryParse

Here's some code review:

---


C#:
//old
static (string RecordIndex, string tag, string IndividualInfo) LineBreakDown(string line, string RecordIndex, string tag, string IndividualInfo)          

//new
static (string RecordIndex, string Tag, string IndividualInfo) ParseLine(string line)

Because:
Use pascal case for named tuple members; it is the c# naming convention for public properties

Do not use method arguments just to create temporary variables. Your "string RecordIndex" etc method arguments are never read

Do not use nouns for method names, use verbs. (I struggle to see how LineBreakdown is a verb; Breakdown can be a verb, but it's an awkward one - BreakdownLine could work but it is also awkward. BreakLineDown is better, but a different verb is more effective here hence ParseLine)

---


C#:
//old
                    string[] lineParts = line.Split(' ');

//new
string[] lineParts = line.Split();

//new2
string[] lineParts = line.Split(' ', 3);
Optional: Split doesn't require an argument if you're splitting on whitespace
Recommended: Split (in more recent versions of .net) can take an argument of the maximum number of sub strings to return. As your code recombines any splits after 2 it's easier and faster to tell split to quit after it has found 2 things to break off

C#:
//old
                    // Get the total number of elements
                    int TotalElements = lineParts.Count();
              
//new
Not necessary. Use lineParts.Length rather than calling Count(). No need to create a temp var for it. Do not use PascalCase for local variables; by convention it is reserved for properties. A seasoned C# developer looking at your code and seeing if(TotalElements > 1) would assume TotalElements is a property defined at the class level. TotalElements could even also be a property defined at class level and then you'd cause a confusion as to which one you were trying to access. Stick to c# conventions; it benefits everyone. The convention for fields defined at class level is to prefix them with underscores, which usefully distinguishes them from local variables

C#:
public int ClassLevelProperty {get;set;}
private int _classLevelField;
private (int SomeInt, string SomeString) _myTuple;
public void SomeMethodName(int methodArgument){
  int localVariable;
}

That's the naming conventions in a nutshell. Keep to them, even when writing classes for eg json deserialisation; don't bend your c# to json norms, use the built in attributes that allow your c# names to differ, so each language keeps to its own conventions.

C#:
//old
                    // if lineParts is not equal to null
                    if (lineParts != null)

//new
Do not write comments that are merely a longer English statement of what the c# code is.

Split never returns null so this check is pointless

C#:
//old

                        switch (TotalElements)
                        {
                            // if the total elements is 2 then parse RecordIndex and tag then move blanks to IndividualInfo field and return control to calling method
                            case 2:
                                RecordIndex = lineParts[0];
                                 tag = lineParts[1];                          
                                 IndividualInfo = "";
                                return (RecordIndex, tag, IndividualInfo);
                          
                            //break;
                            // if the total elements is 3 then parse RecordIndex, tag, and IndividualInfo fields and return control to calling method
                            case 3:
                                RecordIndex = lineParts[0];
                                tag = lineParts[1];
                                IndividualInfo = lineParts[2];
                                return (RecordIndex, tag, IndividualInfo);
                            // if the total elements is greater than 3 then parse the RecordIndex and tag and combine all Individual Info to IndividualInfo
                            default:
                                //ReadLine();
                                RecordIndex = lineParts[0];
                                tag = lineParts[1];
                                int ArrayCount = TotalElements - 2;
                                IndividualInfo = string.Join(" ", lineParts, 2, ArrayCount);
                                return (RecordIndex, tag, IndividualInfo);


                        }
                    }
                    // any problems with method
                    throw new NotImplementedException();


//new
return (
  lineParts[0],
  lineParts[1],
  lineParts.Length > 2 ? lineParts[2] : string.Empty
);
This is simpler than the verbose use of switch: a conditional statement ensures that the array has at least enough elements to satisfy a request for the Nth element and returns it, otherwise if the array lacks an element at eg position 2(because it's length is 2, it only has a position [1]) then return an empty string

If you're not absolutely certain that at least two elements will always exist you can protect the lineparts[1] call in the same way

This approach relies on having passed 3 to string split to stop it splitting any more than 3 parts. If you didn't do that you can call your string.Join(..., 2, length) in the final position instead of lineParts[2]

C#:
return ( ..., ..., lineParts.Length > 2 ? string.Join(" ", lineParts, 2, lineParts.Length-2) : string.Empty);
 
Last edited:
Should I use a Tuple?
If you're going to use it elsewhere or repeatedly then consider a class or record. If you're going to serialize it to json, also consider avoiding tuple because the names in a tuple don't really exist; they're just smoke and mirrors created by the compiler
 
Back
Top Bottom