Alexandr
Alexandr

Reputation: 17

How to use an object from try in catch - c#

I want to use an object in catch block, which get me an exception in try block. I'm parsing some strings to int and need to catch the exception when it's impossible and see, what object was mistaken and in what line. Is that possible or not?

Some code dor example. Thanks.

static void Main(string[] args)
{
    var result = Parse(new List<string>() { "3;5;7", "qwe;3;70" });
}

public static List<int[]> Parse(List<string> list)
{
    try
    {
        return list.Select(str => str.Split(';'))
            .Select(str => Tuple.Create(int.Parse(str[0]), int.Parse(str[1]), int.Parse(str[2])))
            /// something happening
            .ToList();
    }
    catch
    {
        //here in braces I want to know, which element was wrong
        //"qwe" and whole line "qwe;3;70"
        throw new FormatException($"Wrong line [{}]");
    }
}

Upvotes: 1

Views: 2301

Answers (6)

Lostblue
Lostblue

Reputation: 419

I think that you just got a wrong approach here. Yes, using Tuple + Linq would be the laziest way to get your result but you can't generate custom errors as so.

Here is an example of how you can achieve something alike:

    static void Main(string[] args)
    {
        var result = Parse(new List<string>() { "3;5;7", "qwe;3;70" });
    }

    public static List<Tuple<int, int, int>> Parse(List<string> list)
    {
        List<Tuple<int, int, int>> result = new List<Tuple<int, int, int>>();
        int line = 0;
        int errorCol = 0;

        try
        {
            for (line = 0; line < list.Count; line++)
            {
                string[] curentLine = list[line].Split(';');
                int result0, result1, result2;
                errorCol = 1;

                if (curentLine.Length > 0 && int.TryParse(curentLine[0], out result0))
                    errorCol = 2;
                else
                    throw new Exception();

                if (curentLine.Length > 1 && int.TryParse(curentLine[1], out result1))
                    errorCol = 3;
                else
                    throw new Exception();

                if (curentLine.Length > 2 && int.TryParse(curentLine[2], out result2))
                    result.Add(new Tuple<int, int, int>(result0, result1, result2));
                else
                    throw new Exception();
            }

            return result;
        }
        catch
        {
            //here in braces I want to know, which element was wrong
            throw new FormatException("Wrong line " + line + " col" + errorCol);
        }
    }

PS: Line and column start at 0 here.

Upvotes: 0

ProgrammingLlama
ProgrammingLlama

Reputation: 38727

I recommend manually looping through, splitting the data, checking you have enough elements, and then using TryParse on the numbers. I know this is a departure from using Linq, but it's the better way to do this with error checking:

public static List<int[]> Parse(List<string> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
        // you can use nameof(list) instead of "list" in newer versions of C#
    }

    List<int[]> result = new List<int[]>();
    // Loop through the entries
    for (int i = 0; i < list.Count; ++i)
    {
        // Be safe and check we don't have a null value
        // I'm just skipping the 'bad' entries for now but
        // you can throw an error, etc.
        if (list[i] == null)
        {
            // do something about this? (an exception of your choosing, etc.)
            continue;
        }

        // split the entry
        string[] entryData = list[i].Split(';');
        // check we have 3 items
        if (entryData.Length != 3)
        {
            // do something about this?
            continue;
        }

        // try to parse each item in turn
        int a;
        int b;
        int c;

        if (!int.TryParse(entryData[0], out a))
        {
            // do something about this?
            continue;
        }

        if (!int.TryParse(entryData[1], out b))
        {
            // do something about this?
            continue;
        }

        if (!int.TryParse(entryData[2], out c))
        {
            // do something about this?
            continue;
        }

        // add to the results list
        result.Add(new int[] { a, b, c });
    }

    // return the result
    return result;
}

Upvotes: 1

Alessandro D&#39;Andria
Alessandro D&#39;Andria

Reputation: 8868

There are too many problems with your code, you're assuming that parameter list is not null and contains items that can be splitted in 3 strings, and that every string can be safely parsed to int.

If you not have all the above guaranties just check everything:

public static List<int[]> Parse(List<string> list)
{
    if (list == null)
    {
        throw new ArgumentNullException(nameof(list));
    }

    var arrayOfStringArray = list
        .Select(x => x.Split(';'))
        .ToArray();
    var resultList = new List<int[]>();

    for (var i = 0; i < arrayOfStringArray.Length; i++)
    {
        var arrayOfString = arrayOfStringArray[i];

        if (arrayOfString.Length != 3)
        {
            throw new InvalidOperationException("meaningfull message there!");
        }

        var arrayOfInt = new int[3];

        for (var j = 0; j < arrayOfInt.Length; j++)
        {
            arrayOfInt[j] = TryParse(arrayOfString[j], i, j);
        }

        resultList.Add(arrayOfInt);
    }

    return resultList;
}

static int TryParse(string value, int line, int position)
{
    int result;

    if (!int.TryParse(value, out result))
    {
        throw new FormatException($"Item at position {line},{position} is invalid.");
    }

    return result;
}

Upvotes: 0

MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37000

You need a reference to the object causing the exception. However as the instance lives only in the scope of the try-block you can´t access it any more (try and catch don´t share the same scope and thus can´t access the same variables) unless you´d declare the reference to that instance outside the try-bloc

As already mentioned in the comments you should use a normal foreach-loop to have access to the current line:

public static List<int[]> Parse(List<string> list)
{
    var result = new List<int[]>();
    foreach(var str in list)
    {
        try 
        {
            var values = str.Split(';');
            result.Add(Tuple.Create(
                        int.Parse(values[0]), 
                        int.Parse(values[1]), 
                        int.Parse(values[2]))
                    );
        }
        catch
        {
            //here in braces I want to know, which element was wrong
            throw new FormatException($"Wrong line " + str");
        }
    }
    return result;
}

However you can simply avoid all those exceptions by useing TryParse instead which returns false if parsing failed. So this boils down to something like this:

var values = str.Split(';');
int v0, v1, v2;
if(int.TryParse(values[0], out v0 &&
        int.TryParse(values[1], out v1 &&
        int.TryParse(values[2], out v2 &&))
   result.Add(Tuple.Create(v0, v1, v2));
else
    throw new FormatException($"Wrong line " + str");

Upvotes: 1

Dmitry Egorov
Dmitry Egorov

Reputation: 9650

Declare the line and value item counters outside the try/catch block and increase them in the LINQ expression body:

public static List<int[]> Parse(List<string> list)
{
    int line = 0;
    int item = 0;
    try
    {
        return list
            .Select(str => {
                line++;
                item = 0;
                return str
                    .Split(';')
                    .Select(i => { item++; return int.Parse(i); })
                    .ToArray();
            })
            .ToList();
    }
    catch
    {
        throw new FormatException($"Wrong line [{line}]; item [{item}]");
    }
}

Demo: https://dotnetfiddle.net/uGtw7A

Upvotes: 2

Kempeth
Kempeth

Reputation: 1948

Scope is scope. Anything you define inside your try block and don't explicitly pass on is not going to be available in your catch block.

If you need this information you have to iterate manually over the list and try catch each attempt individually...

Upvotes: 0

Related Questions