我有以下对象
var filters = new List<IReportFilter>
{
new ReportFilter
{
ReportColumn = new ReportColumn{ ColumnKey = "Result.IsCompleted"},
Value = "1",
SubFilters = new List<IReportFilter>
{
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ ColumnKey = "User.LastName"}, Value = "Alhayek"},
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ ColumnKey = "User.LastName"}, Value = "Smith"},
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ AggregateFunction = SqlAggregateFunctions.Count}, Type = FilterType.GreaterThenOrEqualTo ,Value = "0" },
}
},
};
Run Code Online (Sandbox Code Playgroud)
obove对象使用类似的方法传递给另一个类
IReportModel ReportModel = Template.CreateReport();
ReportModel.Get(filters);
Run Code Online (Sandbox Code Playgroud)
在类的Get方法内部,ReportModel我想循环遍历filters列表并创建新列表而不更改原始列表.新列表将成为原始列表的子集.
从我的Get方法来看,这就是我所做的
public SqlCommand Build(List<IReportFilter> filters)
{
var a = CloneFilters(filters);
var b = CloneFilters(filters);
List<IReportFilter> standardFilters = ExtractFiltersByAType(a, true);
List<IReportFilter> aggregateFilter = ExtractFiltersByAType(b, false);
}
Run Code Online (Sandbox Code Playgroud)
但每次我执行方法ExtractFiltersByAType的价值a,b以及filters改变等于相同的值aggregateFilter.
我不希望任何变量发生变化.但他们出于某种原因我不明白.
这是我的CloneFilters方法
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter);
}
return copyOfFilters;
}
Run Code Online (Sandbox Code Playgroud)
这是我的 ExtractFiltersByAType
private List<IReportFilter> ExtractFiltersByAType(List<IReportFilter> filtersSource, bool IsStandard = true)
{
List<IReportFilter> validFilters = new List<IReportFilter>();
foreach (var filterSource in filtersSource)
{
if (filterSource.SubFilters != null && filterSource.SubFilters.Any())
{
filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard); //I think this what could be causing this problem
}
if ((IsStandard && !filterSource.ReportColumn.IsAggregate) || (!IsStandard && filterSource.ReportColumn.IsAggregate))
{
validFilters.Add(filterSource);
}
}
return validFilters;
}
Run Code Online (Sandbox Code Playgroud)
题
由于我不是ref通过引用方法来传递对象,为什么我的函数将值更改为原始对象?
将对象列表传递给c#中的方法时,系统是否会创建副本,还是会通过引用传递对象?
我怎样才能解决这个问题,以便每次执行ExtractFiltersByAType方法时,只更改副本而不是原件?
我在想,行filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard);的ExtractFiltersByAType是造成问题,但我不明白为什么,以及如何.
将引用类型作为参数(包括列表)传递时,将引用的副本传递给该对象.这意味着您可以更改对象属性,但不能更改对象本身.
例:
public class Program
{
static void Main(string[] args)
{
Foo foo = new Foo(1);
Console.WriteLine(foo.Bar);
// This will change foo.Bar
ChangeFoo(foo, 5);
Console.WriteLine(foo.Bar);
// Does not change foo
DoesNotChangeFoo(foo, 10);
Console.WriteLine(foo.Bar);
Console.Read();
}
static void ChangeFoo(Foo foo, int newValue)
{
// Since it receives a copy of the reference to Foo, it actually changes foo.Bar value
foo.Bar = newValue;
}
static void DoesNotChangeFoo(Foo foo, int newValue)
{
// Since it receives a copy of the reference to foo, it only updates this method's reference, not changing the caller's reference
foo = new Foo(newValue);
}
}
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
public int Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
如果要更改调用者的对象引用,则需要传递calle使用的实际引用,即使用ref关键字时.
例:
public class Program
{
static void Main(string[] args)
{
Foo foo = new Foo(1);
Console.WriteLine(foo.Bar);
// This will change foo's object reference
ChangeFooObjectReference(ref foo, 15);
Console.WriteLine(foo.Bar);
Console.Read();
}
static void ChangeFooObjectReference(ref Foo foo, int newValue)
{
// SInce you are receiving the actual reference used by the caller, you actually change it's own reference
foo = new Foo(newValue);
}
}
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
public int Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
正如您所假设的那样,问题的主要原因是这一行:
filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard);
Run Code Online (Sandbox Code Playgroud)
这一行实际上改变了这个对象的SubFilters.
但是值得注意的是,克隆方法可能会遇到一些更大的问题.
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter);
}
return copyOfFilters;
}
Run Code Online (Sandbox Code Playgroud)
此方法返回一个新的List,但该列表的内容与参数的内容完全相同.这意味着,如果更改用作参数的对象中包含的任何对象,则也可以在新列表中更改它.
这是一个正在发生的事情的例子.
static void Main(string[] args)
{
List<Foo> foos = new List<Foo>();
foos.Add(new Foo(2));
List<Foo> newFoo = CreateNewFoo(foos);
Console.WriteLine(newFoo.First().Bar);
foos.First().Bar = 5;
// Since we changed the first object of the old list, and it is the same object in the new list, we will get the new result.
Console.WriteLine(newFoo.First().Bar);
Console.Read();
}
static List<Foo> CreateNewFoo(List<Foo> foos)
{
List<Foo> newFoos = new List<Foo>();
foreach(Foo foo in foos)
{
newFoos.Add(foo);
}
return newFoos;
}
Run Code Online (Sandbox Code Playgroud)
我建议在IReportFilter接口中实现ICloneable接口,以及实现IReportFilter的每个具体类.ICloneable实现了一个方法Clone(),它返回一个对象.此方法应该创建一个实现它的同一个类的新实例,其中包含一个与当前对象相同的新对象.比你将你的方法更改为:
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter.Clone() as IReportFilter);
}
return copyOfFilters;
}
Run Code Online (Sandbox Code Playgroud)
至于实现ICloneable接口,请参考这个问题: 实现ICloneable的正确方法
正如用户muratgu在问题评论中所提到的,您的CloneFilter方法正在对列表进行浅层复制,您要查找的是深层复制.这可以通过上述ICloneable接口实现.
| 归档时间: |
|
| 查看次数: |
429 次 |
| 最近记录: |