简体   繁体   English

C#派生类和基本构造函数参数逻辑

[英]C# derived class and base constructor parameter logic

I have a base class: 我有一个基类:

public class Base
{
    public Base(X x ,Y y){
        this.x = x;
        this.y = y;
    }

    public X x{get;}
    public Y y{get;}
}

and a deriver: 和一个派生者:

public class Derive : Base
{
    public Derive(Z z, Q q) :Base (? ?)
    {
    }

    private void ConstructXY(Z z, Q q)
    {
        //Advanced logic for creating an X and a Y
        if(q.something == 5){
            this.x = new X(28); 
        }
        else{
            this.x = new X(25);
        }

        if(this.x.something == 25 && q == 9){
            this.y = new Y(1);
        }
        else{
            this.y = new Y(5)
        }
    }
}

Now I can't correctly call the base constuctor without the "advanced" logic. 现在,如果没有“高级”逻辑,就无法正确地调用基本构造函数。 I used to be able to call ConstructXY() from Derive.ctor() and set x and y from there, this is no longer valid since I removed the x and y setters. 我曾经能够从Derive.ctor()调用ConstructXY()并从那里设置x和y,这不再有效,因为我删除了x和y设置器。 My real-life scenario contains a lot more logic so I am not willing to create a ternary mess. 我的现实生活场景包含更多逻辑,因此我不愿意创建三元混乱。

You can call your "advanced" logic if it fits inside a static method 如果适合静态方法,则可以调用“高级”逻辑

Here is an approach using Tuple available in C# 7 : 这是使用C#7中可用的Tuple的方法:

public class Base
{
    // This constructor was added to avoid calling twice ConstructXY
    public Base((X x, Y y) tuple) :
        this (tuple.x, tuple.y)
    {

    }

    public Base(X x, Y y)
    {
        this.x = x;
        this.y = y;
    }

    public X x { get; }
    public Y y { get; }
}

public class Derive : Base
{
    public Derive(Z z, Q q) : base(ConstructXY(z, q))
    {
    }

    private static (X x, Y y) ConstructXY(Z z, Q q)
    {
        X x;
        Y y;

        //Advanced logic for creating an X and a Y
        if (q.something == 5)
        {
            x = new X(5);
        }
        else
        {
            x = new X(25);
        }

        if (x.something == 25 && q == 9)
        {
            y = new Y(1);
        }
        else
        {
            y = new Y(5)
        }

        return (x, y);
    }
}

If you cannot change the access modifiers of X and Y in the base class so they are accessible for subclasses, then you will have to follow the contract, leaving the constructor the only possible place where you can set those members. 如果您不能在基类中更改XY的访问修饰符,以便子类可以访问它们,那么您将必须遵守合同,将构造函数留在可以设置这些成员的唯一可能的位置。

The only way to add more logic to calculate those values would be to use static methods, for example like this: 添加更多逻辑来计算这些值的唯一方法是使用静态方法,例如:

public class Derive : Base
{
    public Derive(Z z, Q q)
        : base(ConstructX(q), ConstructY(q, z))
    { }

    private static X ConstructX(Q q)
    {
        if (q.something == 5)
            return new X(28);
        else
            return new X(25);
    }

    private static Y ConstructY(Q q, Z z)
    {
        if (z.something == 25 && q.something == 9)
            return new Y(1);
        else
            return new Y(5);
    }
}

Since these are separate method calls, you cannot calculate both values “at once”, so you cannot make the result of Y depend on the result of X without redoing the calculation based on Z and Q again. 由于这些是单独的方法调用,因此您无法“一次”计算两个值,因此,如果不再次基于ZQ重新进行计算,就无法使Y的结果取决于X的结果。

Another way you could solve this is by removing the public constructor on Derive altogether and provide a static factory method instead: 解决此问题的另一种方法是,完全删除Derive上的public构造函数,并提供一个静态工厂方法:

public class Derive : Base
{
    private Derive(X x, Y y)
        : base(x, y)
    { }

    public static Derive Create(Z z, Q q)
    {
        // here you can use your original logic to calculate X and Y
        X x = …
        Y y = …

        return new Derive(x, y);
    }
}

Depending on your complexity of those calculations, this might be the better solution. 根据您的计算复杂度,这可能是更好的解决方案。 If you do need the original Z and Q values, just extend the private constructor to also take those and store them as well: 如果确实需要原始的ZQ值,则只需扩展private构造函数以同时使用它们并存储它们:

private Derive(Z z, Q q, X x, Y y)
    : base(x, y)
{
    this.Z = z;
    this.Q = q;
}

Why not declare your base class setters as private: 为什么不将您的基类设置器声明为私有:

public class Base
{
    public Base(X x ,Y y){
        this.x = x;
        this.y = y;
    }

    public X x{get; private set;}
    public Y y{get; private set;}
}

That way you can still set them in the constructor, and they would not be settable outside? 这样,您仍然可以在构造函数中设置它们,并且它们将无法在外部设置?

I don't really like this way of doing it and would avoid it if possible but you can use a ternary statement in your call to base : 我不太喜欢这种方式,如果可能的话会避免使用,但是您可以在对base的调用中使用三元语句:

public Derive(Z z, Q q)
    : base(new X(xCondition ? 28 : 25), new Y(yCondition ? 1 : 5))

Use static methods to do the conversion. 使用静态方法进行转换。 Like this. 像这样。

public class Derive : Base
{
    public Derive(Z z, Q q) :base (ConvertToX(z, q), ConvertToY(z, q))
    {
    }

    private static X ConvertToX(Z z, Q q) {
        if(q.something == 5){
            return new X(28); 
        }
        return new X(25);
    }

    private static Y ConvertToY(Z z, Q q) {
        // TODO
    }
}

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

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