泛型 类 中具有继承的 C# 方法链接

C# method-chaining in generic classes with inheritance

我有 class A 和 class 继承自 A 的 B 和 C。 我试图像这样实现它们:

public static class Extension
    {
        public static List<T> WithYchangeByX<T>(this List<T> list, string firstX, string newY) where T : A<T>
        {
            for (int i = 0; i < list.Count; i++)
            {
                if (list[i].X.Split(' ')[0].Equals(firstX))
                {
                    list[i] = list[i].WithY(newY);
                }
            }
            return list;
        }
    }

    public abstract class A<T> where T : A<T>
    {
        public string X { get; }
        public string Y { get; }

        abstract public T WithX(string x);

        abstract public T WithY(string y);
    }

    class B: A<B>
    {
        public new string X { get; }
        public new string Y { get; }
        public DateTime Bspecial { get; }
        public static B Default { get; }

        public B(string x, string y, DateTime bSpecial)
            => (X, Y, Bspecial) = (x, y, bSpecial);

        override public B WithX(string x)
            => new B(x, this.Y, this.Bspecial);

        override public B WithY(string y)
            => new B(this.X, y, this.Bspecial);

        public B WithBspecial(DateTime bSpecial)
            => new B(this.X, this.Y, bSpecial);
    }

    class C: A<C>
    {
        public new string X { get; }
        public new string Y { get; }
        public int Cspecial { get; }
        public static C Default { get; }

        public C(string x, string y, int cSpecial)
            => (X, Y, Cspecial) = (x, y, cSpecial);

        override public C WithX(string x)
            => new C(x, this.Y, this.Cspecial);

        override public C WithY(string y)
            => new C(this.X, y, this.Cspecial);

        public C WithCspecial(int cSpecial)
            => new C(this.X, this.Y, cSpecial);
    }

我需要这段代码才能工作:

var b1 = B.Default.WithX("string").WithY("string").WithBspecial(new DateTime(2000, 1, 28));
var b2 = B.Default.WithX("string").WithY("string").WithBspecial(new DateTime(2000, 1, 17));
var c1 = C.Default.WithX("string").WithY("string").WithCspecial(5);

List<A> a1 = new List<A> { b1, b2, c1};

var a2 = a1.WithYchangeByX(x: "string", y: "string");

一切正常,只有 List<A> a1 = new List<A> { b1, b2, c1}; 行不工作。它写了一个错误,我需要使用泛型类型 A,但当我尝试编写它时它是一个无限递归类型。你能以某种方式帮助我吗?谢谢!

您的 WithYchangeByX 方法实际上并不关心泛型参数 T 那么多,因此您可以引入一个非泛型接口 IA,并使其与该接口一起工作相反:

public interface IA
{
    string X { get; }
    string Y { get; }
    IA WithX(string x);
    IA WithY(string y);
}

public abstract class A<T> : IA where T : A<T>
{
    public string X { get; }
    public string Y { get; }

    abstract public T WithX(string x);

    abstract public T WithY(string y);

    IA IA.WithX(string x) => WithX(x);

    IA IA.WithY(string y) => WithY(y);
}

那么你可以这样做:

public static List<IA> WithYchangeByX(this List<IA> list, string firstX, string newY)
{
    for (int i = 0; i < list.Count; i++)
    {
        if (list[i].X.Split(' ')[0].Equals(firstX))
        {
            list[i] = list[i].WithY(newY);
        }
    }
    return list;
}

然后您可以创建一个 List<IA> 并将其传递给此方法。

请注意,现在您丢失了 WithYchangeByXImpl 的通用版本。如果你想保留它,你可以这样做:

private static List<T> WithYchangeByXImpl<T>(List<T> list, string firstX, string newY, Action<int, List<T>> consumer) where T: IA
{
    for (int i = 0; i < list.Count; i++)
    {
        if (list[i].X.Split(' ')[0].Equals(firstX))
        {
            consumer(i, list);
        }
    }
    return list;
}

// these look to be the same, but they actually infer a different type parameter for WithYchangeByXImpl
public static List<IA> WithYchangeByX(this List<IA> list, string firstX, string newY)
    => WithYchangeByXImpl(list, firstX, newY, (i, list) => list[i] = list[i].WithY(newY));

public static List<T> WithYchangeByX<T>(this List<T> list, string firstX, string newY) where T: A<T>
    => WithYchangeByXImpl(list, firstX, newY, (i, list) => list[i] = list[i].WithY(newY));

现在这段代码可以编译:

var b1 = B.Default.WithX("string").WithY("string").WithBspecial(new DateTime(2000, 1, 28));
var b2 = B.Default.WithX("string").WithY("string").WithBspecial(new DateTime(2000, 1, 17));
var c1 = C.Default.WithX("string").WithY("string").WithCspecial(5);

List<IA> a1 = new List<IA> { b1, b2, c1 };

var a2 = a1.WithYchangeByX("string", "string");