简体   繁体   English

使用接口列表时,如何在C#中深度克隆列表?

[英]How can deep clone a list in c# while using a list of interfaces?

I have the following interface 我有以下界面

public interface IReportFilter
{
    IReportColumn ReportColumn { get; set; }

    FilterType Type { get; set; }

    string Value { get; set; }

    string FormattedValue { get; }

    string BuildSqlFilter(string parameterName);

    List<IReportFilter> SubFilters { get; set; }

    FilterOperator SqlOperator { get; set; }

    FilterOperator SubFiltersOperator { get; set; }
}

I have a class that implements it like this 我有一个像这样实现的类

public class ReportFilter : IReportFilter
{
    ...
    ...
}

From another class in my project I have the following code 在我项目的另一个类中,我有以下代码

List<IReportFilter> 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 = "Jones"},
             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" },
        }
    },
};

I like to be able to pass my filters object to a method which will separate my filter based on a condition. 我希望能够将我的filters对象传递给将根据条件分离过滤器的方法。

Here is a method that I write to separate my filters recursively 这是我编写的用于递归分离过滤器的方法

private List<IReportFilter> ExtractFiltersByAType(List<IReportFilter> filters, bool IsStandard = true)
{
    List<IReportFilter> validFilters = new List<IReportFilter>();

    foreach(var filter in filters)
    {

        if (filter.SubFilters != null && filter.SubFilters.Any())
        {
            //At this point we know there are some sub filters
            filter.SubFilters = ExtractFiltersByAType(filter.SubFilters, IsStandard);
        }

        if( (IsStandard && !IsAggregate(filter.ReportColumn.AggregateFunction) ) || (!IsStandard && IsAggregate(filter.ReportColumn.AggregateFunction) ) )
        {
            validFilters.Add(filter);
        }

    }

    return validFilters;
}

The problem with this method is that the variable filters gets passed by reference instead of creating a copy an object. 这种方法的问题在于变量filters是通过引用传递的,而不是创建对象的副本。 Which means any change I make will change the original filters object! 这意味着我所做的任何更改都会更改原始filters对象! I really don't understand this behavior in C# and is making my life miserable! 我真的不了解C#中的这种行为,这使我的生活很痛苦!

Anyhow, I did some research and I found out that I need to deep clone my filters object before passing it to my method. 无论如何,我做了一些研究,发现在将其传递给我的方法之前,我需要深度克隆我的filters对象。

I tried to use the following method to deep clone my object 我尝试使用以下方法深度克隆我的对象

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

Then I used it like so 然后我像这样使用它

var copyOfFilters = Clone(filters);

but this is giving me an error because I my SubFilters object is constructed with an interface! 但这给了我一个错误,因为我的SubFilters对象是用接口构造的!

Could not create an instance of type ReportsEngine.Support.ReportsGenerator.Report.Contracts.IReportFilter. 无法创建类型为ReportsEngine.Support.ReportsGenerator.Report.Contracts.IReportFilter的实例。 Type is an interface or abstract class and cannot be instantiated. 类型是接口或抽象类,无法实例化。 Path '[0].ReportColumn', line 1, position 17. 路径“ [0] .ReportColumn”,第1行,位置17。

How can I correctly clone my object? 如何正确克隆对象?

I am also curios to know which why such behavior exists in c#? 我也很想知道为什么在C#中存在这种行为?

I am also curios to know which why such behavior exists in c#? 我也很想知道为什么在C#中存在这种行为?

You cannot instantiate interfaces. 您不能实例化接口。 Interfaces only give a contract that types needs to adhere to. 接口仅给出类型需要遵守的协定。 It does not give an implementation for that contract though. 但是,它没有提供该合同的实现。

So when you attempt to deserialize some JSON into an interface type, this does not work since the deserializer cannot instantiate that (interface) type. 因此,当您尝试将某些JSON反序列化为接口类型时,此方法将不起作用,因为反序列化器无法实例化该(接口)类型。 It needs a concrete type instead—for example your ReportFilter . 它需要一个具体的类型,例如您的ReportFilter So you could call JsonConvert.DeserializeObject<ReportFilter>(serialized) and it would work. 因此,您可以调用JsonConvert.DeserializeObject<ReportFilter>(serialized) ,它将起作用。

However, since you are doing this in a generic method, you attempt to deserialize it as type T . 但是,由于使用通用方法执行此操作,因此您尝试将其反序列化为类型T That type T comes from the generic type argument of the method which is inferred from the call to that method. 类型T来自该方法的泛型类型参数,该类型参数是从对该方法的调用中推断出来的。 In this case, you are likely passing a IReportFilter object, so at compile time that's the type that's being used for T (regardless of whether that object is an actual ReportFilter ). 在这种情况下,您可能会传递IReportFilter对象,因此在编译时 ,这就是用于T的类型(无论该对象是否是实际的ReportFilter )。

In order to change that, you would have to call it explicitly as a ReportFilter : 为了更改它,您必须将其显式调用为ReportFilter

var copyOfFilter = Clone((ReportFilter)filter);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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