将相同属性添加到两个类的最佳方法

时间:2017-12-29 06:35:22

标签: c#

我有类似的类结构:

Array
(
    [0] = Array
        (            
            [tutor] = Array
                (                    
                    [per_hour] =50                    
                    [gender] = F
               )

        )

    [1] => Array
        (


            [tutor] =Array
                (

                    [per_hour] = 50

                    [gender] = M

                )

        )

    [2] => Array
        (

            [tutor] =Array
                (                   
                    [per_hour] = 818

                    [gender] = F

                )

        ) 

)

现在这些类在webapi控制器中使用,一个操作返回public class Base { public string PropBase { get; set; } } public class Derived : Base { public string PropDerived { get; set; } } 和另一个Base

我希望有另一个具有相同操作结构的控制器(并且会返回两个模型),但我想为这两个模型添加另一个属性。

我想过像

这样的东西
Derived

但这对我来说看起来并不干净。

我真的不想为这两个操作使用更具体的public class BaseSpecific : Base { public string Common { get; set; } } public class DerivedSpecific : Derived { public string Common { get; set; } } 类,因为它会混淆代码的意图。

最好的方法是什么?

P.S。当然,这个例子过于简化了。控制器存在于两个不同的项目中,模型存在于两者共享的nuget包中。

5 个答案:

答案 0 :(得分:1)

对DRY的真正含义似乎存在一些困惑。 DRY并不直接关注代码重复 - 这仅仅是一种症状。 DRY关注业务逻辑的重复。使用重复或完全不同的代码复制业务逻辑是完全可能的(例如,有3种不同的方式将用户登录到您的应用程序中)。

对于这样的问题,您可以问自己最重要的问题是:这两个(或更多)事情是否能够独立变化?例如,如果我更改x的行为,y的行为是否也会发生变化?如果答案是no,那么它们在概念上是不同的。

仅仅因为这些属性目前恰好相同,并不意味着它们代表了对DRY的违反。如果他们有不同的责任,他们在撰写本文时恰好相同。

优先考虑组合而非继承是允许您的类型彼此独立变化的原因。继承会导致诸如您现在所拥有的问题,您真正希望BaseSpecific以不同的方式改变DerivedSpecific,但是您要阻止这些类型,因为它们构建在顶部Base。组合还允许您在其他场景中重用更多逻辑,这可以促进应用程序中更具延展性的结构。

解决方案?拆分你的类型。不要共享属性,因为BaseSpecificDerivedSpecific已经告诉您他们想要独立变化。

答案 1 :(得分:0)

您可以做的就是创建另一个类,并使这两个类扩展新类的实现。这样他们将从扩展类继承属性,它仍然看起来很健壮

现在我更多地考虑它,而不是派生,为什么不在Base中创建一个对象呢?如 public Derived ObjDerived {get;组; }

答案 2 :(得分:0)

我确信您已经知道,C#(。Net)没有多重继承。

您需要导出基类的中间版本,使用接口(实际上不会解决任何问题),或者只是重新创建基类

今天没有免费的午餐抱歉

答案 3 :(得分:0)

答案其实非常非常简单。如果它只是一个属性,请将它添加到两个类中,并且根本不担心继承。

我会避免使用继承作为在不同用途的类之间共享公共属性的方法。最终它会导致越来越多的课程层次,并且稍后一个课程最终会得到一个它不需要的属性,因为我们想要继承A,B和C,我们无法做到没有继承D也是。

假设您有两个或多个属性,这些属性在多个类之间具有共同的含义或目的,否则这些属性不同。在这种情况下,您可以将这些属性组合成一个单独的类,如

public class RecordStatus
{
    public bool Active { get; set; }
    public DateTime Created { get; set; }
    public DateTime Updated{ get; set; }
}

然后,不同的类可以将其实例作为属性。他们仍然没有必要继承它。公共类的使用仅用于表明该属性在其出现的任何地方都具有共同的含义。它还可以防止向类添加多个属性的额外工作。现在你可以添加一个。

当您只需要跨类重用属性或方法时,组合是一种更好的方法。将属性或方法放在类中,然后在需要的地方添加这些类的实例。

在对象将要向下转换的情况下,继承可以更好地工作。也许在某些方法中,您希望以不同方式处理派生类,但在其他方法中,您希望拥有基类的集合并对其进行操作,而无需关心派生类。

即使这样,您也可能不需要使用继承。也许你可以让这些类实现一个通用的接口。也许在需要不同类来实现公共接口的情况下,您可以将它们包装在另一个实现接口的类中,并使内部类适应外部接口。这些方法中的任何一种都避免使用两个或更多不相关的类,并通过使它们从公共基础继承来耦合它们。

许多人发现继承有用的情况比他们最初想的要少。我已经看到,只需要进行一些更改,看起来像继承的明确案例就会失控,因为派生类的共同点越来越少,树状结构变成了灌木丛或网络。

答案 4 :(得分:0)

在没有谈论继承的情况下,您似乎只想在从控制器返回时添加属性,我假设为序列化值

如果你真的需要在答案中添加一些属性,你可以这样做:

    public class Base
    {
        public string PropBase { get; set; }
    }

    public class Derived : Base
    {
        public string PropDerived { get; set; }
    }

    [JsonConverter(typeof(SpecificConverter))]
    [DataContract]
    public class Specific
    {
        public Specific(object obj)
        {
            Object = obj;
        }
        public bool Common { get; set; }

        public object Object { get; set; }
    }

    public class SpecificConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Specific obj = value as Specific;
            JToken t = JToken.FromObject(obj.Object);
            t.First.AddBeforeSelf(new JProperty("Common", obj.Common));
            t.WriteTo(writer);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
        }

        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanConvert(Type objectType)
        {
            return typeof(Specific).IsAssignableFrom(objectType);
        }
    }
}

然后,只需从您的控制器返回new Specific(myObject)(或制作一些扩展方法),然后从控制器返回正确的序列化值

返回派生对象:

{"PropDerived":"derived","PropBase":"base"}

从之前的派生中返回特定:

{"Common":"common","PropDerived":"derived","PropBase":"base"}

如果需要其他序列化格式,您可以为xml或其他格式做同样的事情

我认为可以使用具有T对象的Specific,但由于Specific obj = value as Specific;这更麻烦 您可以轻松地使用动态,这将起作用

[JsonConverter(typeof(SpecificConverter))]
[DataContract]
public class Specific<T>
{
    public Specific(T obj)
    {
        Object = obj;
    }
    public string Common { get; set; } = "common";

    public T Object { get; set; }
}
...
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    dynamic dyn = value;
    JToken t = JToken.FromObject(dyn.Object);
    t.First.AddBeforeSelf(new JProperty("Common", dyn.Common));
    t.WriteTo(writer);
}
...
public override bool CanConvert(Type objectType)
{
    return typeof(Specific<>).IsAssignableFrom(objectType);
}
...
public static class SepcificHelper
{
    public static Specific<T> ToSpecific<T>(T obj)
    {
        return new Specific<T>(obj);
    }
}

然后返回&#34; myDerived.ToSpecific()&#34;

如果整个控制器上的公共属性相同,您还可以在控制器上设置特定的序列化程序,只需返回对象并在specificSerializer中执行操作