如何在使用接口列表的同时在c#中深度克隆列表

本文关键字:列表 深度 接口 | 更新日期: 2023-09-27 17:57:26

我有以下接口

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; }
}

我有一个类实现它,就像这个

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

在我的项目中的另一个类中,我有以下代码

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" },
        }
    },
};

我希望能够将我的filters对象传递给一个方法,该方法将根据条件分离我的过滤器。

下面是我写的一个方法,递归地分离我的过滤器

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;
}

此方法的问题是通过引用传递变量filters,而不是创建对象的副本。这意味着我所做的任何更改都将更改原始filters对象!我真的不理解C#中的这种行为,这让我的生活变得悲惨!

无论如何,我做了一些研究,发现在将filters对象传递给我的方法之前,我需要对其进行深度克隆。

我尝试使用以下方法来深度克隆我的对象

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

然后我就这么用

var copyOfFilters = Clone(filters);

但这给了我一个错误,因为我的SubFilters对象是用接口构造的!

无法创建类型为的实例报表引擎。支持ReportsGenerator。汇报合同。IReportFilter。类型是接口或抽象类,不能实例化。路径"[0]"。ReportColumn’,第1行,位置17。

如何正确克隆对象?

我也很好奇为什么c中会有这样的行为?

如何在使用接口列表的同时在c#中深度克隆列表

我也很好奇为什么c中会有这样的行为?

不能实例化接口。接口只提供类型需要遵守的约定。但它没有提供该约定的实现。

因此,当您试图将某些JSON反序列化为接口类型时,这是不起作用的,因为反序列化程序无法实例化该(接口)类型。它需要一个具体的类型,例如ReportFilter。因此,可以调用JsonConvert.DeserializeObject<ReportFilter>(serialized),它就会工作。

但是,由于您在泛型方法中执行此操作,因此尝试将其反序列化为类型T。该类型T来自该方法的泛型类型参数,该参数是从对该方法的调用中推断出来的。在这种情况下,您可能会传递一个IReportFilter对象,因此在编译时这是用于T的类型(无论该对象是否为实际的ReportFilter)。

为了改变这一点,您必须将其明确地称为ReportFilter:

var copyOfFilter = Clone((ReportFilter)filter);