简体   繁体   中英

How to return a value from a function but keeping the function “alive” c#

I have a function in a DLL that I've created that executes SQL commands and returns the path file from where they were executed, the number of commands with errors and the number of commands executed sucessfully all in a 3 pos. array. The problem is that it only returns when the foreach ends wich will only allow me to present in a text box in the form the last executed file, and I need to show the file path as they are executed.

I need a way to return the array everytime the foreach iterates but keeping >the function alive so it executes the rest of the commands in the next >files, this is the function:

public string[] ExecuteCommands(string Directoria, CdpsiUpdateSql Updater, CdpsiUpdateSqlparser parser, string Log)
        {

            string[] numArray1 = new string[3];
            List<string> list = ((IEnumerable<string>)Directory.GetFiles(Directoria, "*.sql", SearchOption.TopDirectoryOnly)).Select(f =>
            {
                string[] strArray = Path.GetFileName(f).Split('_');
                int result;
                if (strArray.Length < 1 || !int.TryParse(strArray[0], out result))
                    result = -1;
                var data = new
                {
                    File = f,
                    Version = result
                };
                return data;
            }).Where(f => f.Version > -1).OrderBy(f => f.Version).Select(f => f.File).ToList<string>();
            foreach (string str in list)
            {
                int[] numArray2 = this.ExecuteCommand(parser.Parser(str), Updater, str, Log);
                int Certos = Convert.ToInt32(numArray1[0]);
                int Errados = Convert.ToInt32(numArray1[1]);
                Certos  += numArray2[0];
                Errados += numArray2[1];
                numArray1[0] = Certos.ToString();
                numArray1[1] = Errados.ToString();
                numArray1[2] = str;

            }
            return numArray1;
        }

Any help will be higly aprecciated, thank you


using yield?

public IEnumerable<string []> ExecuteCommands(string Directoria, CdpsiUpdateSql Updater, CdpsiUpdateSqlparser parser, string Log)
        {

            string[] numArray1 = new string[3];
            List<string> list = ((IEnumerable<string>)Directory.GetFiles(Directoria, "*.sql", SearchOption.TopDirectoryOnly)).Select(f =>
            {
                string[] strArray = Path.GetFileName(f).Split('_');
                int result;
                if (strArray.Length < 1 || !int.TryParse(strArray[0], out result))
                    result = -1;
                var data = new
                {
                    File = f,
                    Version = result
                };
                return data;
            }).Where(f => f.Version > -1).OrderBy(f => f.Version).Select(f => f.File).ToList<string>();
            foreach (string str in list)
            {
                int[] numArray2 = this.ExecuteCommand(parser.Parser(str), Updater, str, Log);
                int Certos = Convert.ToInt32(numArray1[0]);
                int Errados = Convert.ToInt32(numArray1[1]);
                Certos  += numArray2[0];
                Errados += numArray2[1];
                numArray1[0] = Certos.ToString();
                numArray1[1] = Errados.ToString();
                numArray1[2] = str;
                yield return numArray1;
            }
            //return numArray1;
        }

At first I would define a better type for the result like that:

public struct Result
{
    public string File;
    public int Errors;
    public int Successes;
}

And then you could add a IProgress<T> parameter to your dll method: And use this as an extra argument to your dll method:

public void ExecuteCommands(string Directoria, CdpsiUpdateSql Updater, CdpsiUpdateSqlparser parser, string Log, IProgress<Result> progress)
{
    //... your code

    foreach (string str in list)
    {
        int[] numArray2 = this.ExecuteCommand(parser.Parser(str), Updater, str, Log);
        // your code

        Result result = new Result
        {
            File = str,
            Error = Errados,
            Successes = Certos
        };
        progress.Report(result);
    }
}

On the calling site you create an instance of Progress<T> like that:

Progress<Result> progress = new Progress<Result>(HandleProgress);
ExecuteCommands(Directoria, Updater, parser, Log, progress);

And implement a HandleProgress method like that:

private void HandleProgress(Result result)
{
    /* use the values in result to update your UI */
    Refresh(); // refresh the controls you updated
}

Note however that your ExecuteCommands runs synchronously . So your UI thread is blocked and changes to the controls are not immediatly drawn. Therefor I added that Refresh() statement, but its exact implementation depends a little on the controls you are about to update.

The yield/IEnumerable approach is a good one, too. But I still would suggest a proper Result type. The consumers of your method will never know what information is contained in that string array. And the problem with the blocked UI thread will be the same.

You can move your return statement into the foreach loop and use yield

foreach (string str in list)
            {
                int[] numArray2 = this.ExecuteCommand(parser.Parser(str), Updater, str, Log);
                int Certos = Convert.ToInt32(numArray1[0]);
                int Errados = Convert.ToInt32(numArray1[1]);
                Certos  += numArray2[0];
                Errados += numArray2[1];
                numArray1[0] = Certos.ToString();
                numArray1[1] = Errados.ToString();
                numArray1[2] = str;

                yield return numArray1;
            }

Just to provide you with some alternatives. You can try using delegate/ lambda

public void ExecuteCommands(string Directoria, CdpsiUpdateSql Updater, CdpsiUpdateSqlparser parser, string Log, Action<int[]> getResults)
{

    string[] numArray1 = new string[3];
    List<string> list = ((IEnumerable<string>)Directory.GetFiles(Directoria, "*.sql", SearchOption.TopDirectoryOnly)).Select(f =>
    {
        string[] strArray = Path.GetFileName(f).Split('_');
        int result;
        if (strArray.Length < 1 || !int.TryParse(strArray[0], out result))
            result = -1;
        var data = new
        {
            File = f,
            Version = result
        };
        return data;
    }).Where(f => f.Version > -1).OrderBy(f => f.Version).Select(f => f.File).ToList<string>();
    foreach (string str in list)
    {
        int[] numArray2 = this.ExecuteCommand(parser.Parser(str), Updater, str, Log);
        int Certos = Convert.ToInt32(numArray1[0]);
        int Errados = Convert.ToInt32(numArray1[1]);
        Certos += numArray2[0];
        Errados += numArray2[1];
        numArray1[0] = Certos.ToString();
        numArray1[1] = Errados.ToString();
        numArray1[2] = str;

        //new code
        if(getResults!=null)
        {
            getResults(numArray1);
        }
    }

}

Usage in your main call will then be:

int totalResults=0;
Action<int[]> doSomething = (results)=>
    {
        //do some work whenever results is returned, e.g
        totalResults+=results.Sum();
    };
ExecuteCommands(directoria, parser, updater, log, doSomething);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM