如何编写一个可以由两个非继承类共享的方法

时间:2011-10-17 14:45:06

标签: c# .net refactoring polymorphism

我有两个类,两个类都有相同的方法(名称+类型+行为)和相同的属性(名称+类型)

public class Country
{
    public string Name { get; set; }

    public void DisplayName()
    {
        Console.WriteLine(this.Name);
    }
}

public class Person
{
    public string Name { get; set; }

    public void DisplayName()
    {
        Console.WriteLine(this.Name);
    }
}

- 不允许PersonCountry类继承

在上面的代码中,您可以看到Person类具有类似DisplayName类的方法(Country)。我正在寻找一种方法,以便两个类可以共享相同的方法代码,我想这样做,因为在我的真实代码 - 我想要分享的方法是非常大的,每当我在一个类中更改代码我必须复制粘贴它也在其他课堂上。我觉得这不是正确的方法。

请建议如何解决此问题。

10 个答案:

答案 0 :(得分:2)

你说他们不能从公共基类继承,但你可以添加一个接口,对吗?我建议给每个人一个共同的界面。然后为该界面定义 extension method 。该方法将在VS中为每个人显示。

Assumption :如果扩展方法访问的类成员是公共的或内部的,那么这将有效。)

interface IDisplayable
{
    string Name {get; set;}
}

public class Country : IDisplayable
{
    public string Name { get; set; }
}

public class Person : IDisplayable
{
    public string Name { get; set; }
}

public static void DisplayName(this iDisplayable d)
{
    return doSomeDisplayLogic(d.Name);
}

。 。 。在与扩展方法相同的类中,定义(不作为扩展方法)函数doSomeDisplayLogic来执行您的通用逻辑。 (第一次遇到问题:确保扩展方法在同一个命名空间中,或者它的命名空间也包含在调用代码中。)

我不知道你是否对扩展方法不熟悉。它们非常强大。 (和许多强大的功能一样,它们可能被滥用)。 extension method on an interface一开始似乎很疯狂,直到你直接了解扩展方法是如何工作的。没有这个,LINQ就行不通了!

更新:我在上面看到你的评论,这些类不能从公共类继承,因为它们已经从一个公共类继承(我认为它不能过多地混淆)。我想指出选项2 ,基于此:创建一个国家/人/等的新类。将继承,它本身继承自现有的公共父类。现有的基类将成为祖父母类,可以这么说。如果 Country Person 除了此DisplayName方法之外还有其他共同特征,那么这将成为更多的路径。如果您正在使用DisplayName,那么接口/扩展模式可能会更好。

答案 1 :(得分:1)

我建议在某些情况下避免使用扩展方法,当你需要对这两个类略有不同的实现时,你可能会遇到问题,然后你必须设计一个更通用的解决方案,EM会导致像多重继承一样的问题确实

作为更通用的OOD解决方案,我建议将此行为提取到由接口抽象的单独服务类中:

public interface IDisplayService()
{
    void Display();
}

然后通过构造函数实现它并注入到这两个类中。

此外,您可以通过构造函数或甚至属性注入ActionFunc<>,而不是引入接口和新类,然后通过调用注入的委托来调用此方法。

答案 2 :(得分:1)

定义界面

public interface INameable
{
    string Name {get;}
}

然后添加扩展名

public static class INameableExt
{
    public static void DisplayName(this INameable n)
    {
        // do your thing
    }
}

答案 3 :(得分:0)

有两种选择:

  • 让两个类为公共成员(Name)实现一个接口,并为行为添加扩展方法(或者只是一个普通的静态方法)
  • 创建采用实例和lambda exppession访问注释成员的方法,例如

    public static void Display<T>(T item, Func<T, string> nameGetter)
    

    然后用(比如说)

    来调用它
    DisplayHelper.Display(person, p => p.Name);
    

界面解决方案更清晰,但使用委托更灵活 - 您不需要能够更改所涉及的类,并且您可以应对小的变化(例如PersonName vs {{ 1}} vs FooName

答案 4 :(得分:0)

您可以创建一个静态实用程序方法DisplayName(),您可以传递显示所需的数据,或者使用组合并将所有属性和相应的方法(例如DisplayName())移动到单独的类中 - 然后使用来自CountryPerson的此类的实例。

答案 5 :(得分:0)

您可以在单独的类中定义该big方法,然后在上述两个类中调用该方法。对于静态方法,您可以使用classname.methodname()语法调用该方法。

对于非静态方法,您必须这样做:

classname obj=new classname();
obj.methodname();

答案 6 :(得分:0)

您可以实施策略模式:

class DisplayNameStrategy<T> {
    private readonly Func<T, string> nameSelector;
    public void DisplayNameStrategy(Func<T, string> nameSelector) {
        this.nameSelector = nameSelector;
    }

    public void abstract DisplayName(T t);
}

class WriteToConsoleDisplayNameStrategy<T> : DisplayNameStrategy<T> {
    public void WriteToConsoleDisplayNameStrategy(Func<T, string> nameSelector)
        : base(nameSelector) { }
    public override void DisplayName(T t) {
        Console.WriteLine(this.nameSelector(t));
}

public class Person {
    private readonly DisplayNameStrategy<Person> displayNameStrategy =
        new WriteToConsoleDisplayNameStrategy<Person>(x => x.Name);

    public string Name { get; set; }

    public void DisplayName() {
        this.displayNameStrategy(this);
    }
}

注意:注入具体策略可能更好。

答案 7 :(得分:0)

我想要的是C#中不允许的多类继承。 (但可以使用C ++,你没有这样做。)

所有其他人都已确定做了INTERFACE解决方案,可能是最好的方法。但是,根据您的描述,无论对象的类型是个人还是企业,您都拥有相同的单一代码块。并且您对大量代码的引用,您不希望在所有其他类中复制/粘贴相同的确切代码,这些类可能会使用类似的常见“事物”。

举个简单的例子,您有一个功能可以构建一个人的姓名和地址(或公司名称和地址)。您的代码需要一个名称和最多3个地址行,以及城市,州,邮政编码(或其他任何内容)。因此,这样的名称/地址信息的格式对于人与企业是相同的。您不希望在这两者之间反复复制这种确切的方法。但是,每个班级仍然有自己负责的事情。

我知道它是一个简单的上下文示例,但我认为可以解决这个问题。

仅定义接口的问题在于它不允许您实际实现您所指的CODE。

从您的示例中,我会考虑做一些事情的组合。创建一个静态类,其中包含您可能希望“全局”可用的方法。允许将一个参数传递给一个类的实例,该类具有一个接口类型,所有其他人都已表达,这将保证传入对象具有您期望的所有“部分”属性/方法,并使IT运行它根据需要。像

这样的东西
public interface ITheyHaveInCommon
{
   string Name;
   string GetOtherValue();
   int SomethingElse;
}

public class Person : ITheyHaveInCommon
{
  // rest of your delcarations for the required contract elements
  // of the ITheyHaveInCommon interface...
}

public class Country : ITheyHaveInCommon
{
  // rest of your delcarations for the required contract elements
  // of the ITheyHaveInCommon interface...
}


public static class MyGlobalFunctions
{
   public static string CommonFunction1( ITheyHaveInCommon incomingParm )
   {
      // now, you can act on ANY type of control that uses the 
      // ITheyHaveInCommon interface...
      string Test = incomingParm.Name
                  + incomingParm.GetOtherValue()
                  + incomingParm.SomethingElse.ToString();

      // blah blah with whatever else is in your "huge" function

      return Test;
   }
}

答案 8 :(得分:0)

您可以使用组合:定义接口,实现它的类,然后让PersonCountry通过调用实现类上的方法来实现接口:

// the interface
public interface IName {
    string Name { get; set; }
    void DisplayName();
}

// a class that implements the interface with actual code
public class NameImpl : IName {
    public string Name { get; set; }

    public void DisplayName() {
        Console.WriteLine(this.Name);
    }
}

public class Country : IName {

    // instance of the class that actually implements the interface
    IName iname = new NameImpl();

    // forward calls to implementation
    public string Name {
        get { return iname.Name; }
        set { iname.Name = value; }
    }

    public void DisplayName() {
        // forward calls to implementation
        iname.DisplayName();
    }
}

答案 9 :(得分:0)

警告:这里有很多未经测试的代码,因为我不同意基础假设“没有继承”,因此大肆猜测。

这样的事情可以帮助你。创建一个新的静态类并将代码粘贴到此处。

public static class Display
{
    public static void DisplayName<T>(T obj)
    {
        if ((T is Person) || (T is Country) || (T is whateveryouwant))
        {
            //do stuff
        }
    }
}

在您的类中,重构ShowDisplayName()以使用“this”作为参数调用它。

...     public void DisplayName()     {          显示名称(本);     } ...

我想知道为什么你的类不允许从基类继承它,因为这是解决这个问题的最合适方法。