Home > Software engineering >  How to use a reference type value in a loop body
How to use a reference type value in a loop body

Time:10-07

I introduced an interface in the constructor, then looped through a method to call the interface's methods, and finally used the interface's public properties, but this property is always changing. I know it's because of the reference variable. Like the code below, is there a better design pattern to implement. thank you very much!

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

var f = new Factory(new StdOperation());
var stdCollection = new List<Std>{ new Std("aa"),
    new Std("bb/cc"),
    new Std("cc/dd"),
    new Std("dd/ee"),
    new Std("ee/ff")
};

f.Do(stdCollection);

foreach (var item in stdCollection)
{
    Console.WriteLine(item.NameAry?.Count);
}
//Output  0 0 0 2

public class Std
{
    public Std(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public List<string> NameAry { get; set; }
}

public class Factory
{
    private readonly IStudentOperation stdOpt;

    public Factory(IStudentOperation stdOpt)
    {
        this.stdOpt = stdOpt;
    }

    public void Do(List<Std> stdAry)
    {
        var result = new List<string>();
        foreach (var item in stdAry)
        {
            if (!this.stdOpt.IsMatch(item.Name))
            {
                continue;
            }
            item.NameAry = this.stdOpt.NameAry;
        }
    }
}

public interface IStudentOperation
{
    List<string> NameAry { get; }

    bool IsMatch(string name);
}

public class StdOperation : IStudentOperation
{
    private List<string> _nameAry;

    public bool IsMatch(string name)
    {
        this._nameAry?.Clear();

        //do something...
        if (name.IndexOf("/") == -1)
        {
            return false;
        }
        this._nameAry = name.Split("/").ToList();
        return true;
    }

    public List<string> NameAry => this._nameAry;
}

CodePudding user response:

The reason for your issue is:

item.NameAry = this.stdOpt.NameAry;

Factory has a method Do that takes a whole list but has just a single IStudentOperation with a field List<string> NameAry. So you iterate one list and for every item in it you assign the same result list, so only the last one is correct.

Instead you could let IsMatch return the NameAry as an out-parameter:

public interface IStudentOperation
{
    bool IsMatch(string name, out List<string> matches);
}

public class StdOperation : IStudentOperation
{
    public bool IsMatch(string name, out List<string> matches)
    {
        if (name.IndexOf("/") == -1)
        {
            matches = new List<string>(0);
            return false;
        }
        matches = name.Split("/").ToList();
        return true;
    }
}

Now Do can consume it in this way:

public void Do(List<Std> stdAry)
{
    foreach (var item in stdAry)
    {
        if (!this.stdOpt.IsMatch(item.Name, out var matches))
        {
            continue;
        }
        item.NameAry = matches;
    }
}

Now the result is as expected:

2
2
2
2
  • Related