【问题标题】:Implementations of an Interface with Different Types?不同类型的接口的实现?
【发布时间】:2011-07-08 21:35:17
【问题描述】:

尽我所能搜索,但不幸的是我没有学到任何相关的东西;基本上我正在尝试解决 C# 中的以下问题...

例如,我有三个可能的引用(refA、refB、refC),我需要根据配置选项加载正确的一个。但是到目前为止,我看不到一种不需要我在整个代码中使用所述引用对象的名称的方法(提供了引用的对象,我无法更改它们)。希望下面的代码更有意义:

public ??? LoadedClass;

public Init()
    {
        /* load the object, according to which version we need... */
        if (Config.Version == "refA")
        {
            Namespace.refA LoadedClass = new refA();
        }
        else if (Config.Version == "refB")
        {
            Namespace.refB LoadedClass = new refB();
        }
        else if (Config.Version == "refC")
        {
            Namespace.refC LoadedClass = new refC();
        }

        Run();
    }
private void Run(){
    {
        LoadedClass.SomeProperty...
        LoadedClass.SomeMethod(){ etc... }
    }

如您所见,我需要将 Loaded 类公开,因此我尝试在加载所需的真实类时“动态”更改类型。 refA、refB 和 refC 中的每一个都将实现相同的属性和方法,但名称不同。同样,这是我正在使用的,而不是我的设计。

话虽如此,我试图了解接口(听起来像是我所追求的),但我正在查看它们并看到严格的类型 - 这对我来说很有意义,即使它不是对我有用。

欢迎任何和所有想法和意见,如有必要,我会澄清任何事情。请原谅我在术语上犯的任何愚蠢错误,我是第一次学习所有这些。到目前为止,我真的很喜欢使用 OOP 语言 - 来自 PHP 的这些东西让我大吃一惊:-)

编辑

很抱歉根本没有说清楚,但是 refA、refB、refC 中的每一个都有其方法的独特类型,即使它们在功能方面基本上是相同的(想法是它们是版本化的)。这意味着有了一个界面,在我看来,我只剩下:

public interface IRef
    {
        SomeType<<RefA,RefB,RefC,???>> SomeProperty {get;}
        void SomeMethod();
    }

感谢 GenericTypeTea 提供的起始接口点。我可能正在尝试做一些不推荐/不可能的事情......根本问题是我需要支持来自同一程序的不同版本(根据配置选项);或者我将为每个版本开发“不同”的程序,这只是一个可怕的混乱:-)

编辑 2

public interface Sage
    {
        SageDataObject???.SDOEngine sdo;
    }

class SageObj150 : Sage
    {
        SageDataObject150.SDOEngine sdo = new SageDataObject150.SDOEngine();
    }

class SageObj160 : Sage
    {
        SageDataObject160.SDOEngine sdo = new SageDataObject160.SDOEngine();
    }

class SageObj170 : Sage
    {
        SageDataObject170.SDOEngine sdo = new SageDataObject170.SDOEngine();
    }

其中 SDOEngine() (我刚刚看到)是一个接口本身 - 我认为这会使事情变得非常混乱。从不了解 C# 的情况下,我认为我在编写整个应用程序方面做得很好,但现在这似乎超出了我的 Google 技能。提前感谢大家的耐心等待!

【问题讨论】:

  • “refA、refB 和 refC 中的每一个都将实现相同的属性和方法但名称不同。” 我解释为属性和方法不同,但现在我猜你可能只是说refA refBrefC 有不同的名称?
  • 所以SomeType 可能是stringbool 或'int'?
  • 不完全是,它们都是每个 refA、refB、refC 的特定对象。
  • 现在它开始闻起来像是应该使用generics 解决的解决方案,或者使用我建议的adapter pattern。当然,只有如果你可以定义一个通用适配器,这取决于类型转换。
  • @b3njamin - 在不了解SomeType&lt;&lt;RefA,RefB,RefC,???&gt;&gt; SomeProperty {get;} 的情况下,我真的无法提供进一步的解决方案。也许您应该发布 RefA、RefB 和 RefC 的代码?

标签: c# oop class types


【解决方案1】:

如果这些类没有共同的祖先并且具有不同的方法名称,您可以对它们中的每一个进行子类化,实现一个公共接口来创建一个代理对象。

interface ICommonFunctions
{
   void MethodA();

   void MethodB();
}

class ProxyRefA : ICommonFunctions
{
    refA proxyObj = new refA;

    void MethodA() { proxyObj.methodWithOtherName(); }

    void MethodB() { proxyObj.otherMethodName(); }
}

/* The same for refB and refC */

等等。

这样你的原始代码就可以拥有:

public ICommonFunctions LoadedClass;

...

编辑:实施 Steven Jeuris 的建议。在代理类中使用组合而不是继承。

【讨论】:

  • 代理类不应从该类扩展,而应将其作为成员。
  • @Steven Jeuris 是的,我认为组合可能比继承更好。我没想到。
【解决方案2】:

对我来说,factory pattern 似乎是一个很好的机会。创建您的通用接口,将其提供给所有三个类,然后启动正确的类。这些受界面约束,因此您实际上不必做任何太奇怪的事情。

class Program
    {
        static void Main(string[] args)
        {
            RefFactory factory = new RefFactory();
            ICommonFunctionality a = factory.Create(0);
            Console.WriteLine(a.SomeMethod());

            ICommonFunctionality b = factory.Create(1);
            Console.WriteLine(b.SomeMethod());

            ICommonFunctionality c = factory.Create(2);
            Console.WriteLine(c.SomeMethod());

            //The above is to just test. Should be something like this:
            ICommonFunctionality Ref;

            if (1 == 1)
            {
                Ref = factory.Create(0);
            }
            if (1 == 2)
            {
                Ref = factory.Create(1);
            }

            //etc..

            Console.Read();
        }
    }

    public class RefFactory
    {
        public ICommonFunctionality Create(int someCondition)
        {
            if (someCondition == 0)
            {
                return new RefA();
            }
            else if (someCondition == 1)
            {
                return new RefB();
            }
            else
            {
                return new RefC();
            }
        }
    }

    public interface ICommonFunctionality
    {
        bool SomeProperty { get; set; }
        string SomeMethod();
    }

    public class RefA : ICommonFunctionality
    {
        public bool SomeProperty { get; set; }
        public string SomeMethod()
        {
            return "RefA";
        }
    }

    public class RefB : ICommonFunctionality
    {
        public bool SomeProperty { get; set; }
        public string SomeMethod()
        {
            return "RefB";
        }
    }

    public class RefC : ICommonFunctionality
    {
        public bool SomeProperty { get; set; }
        public string SomeMethod()
        {
            return "RefC";
        }
    }

【讨论】:

    【解决方案3】:

    我建议为不同的班级写一个共同的adapter。然后,您将拥有一个通用接口,该接口处理访问使用的特定命名基础属性/方法。

    更新:

    “每个 refA、refB 和 refC 都将实现相同的属性和方法,但具有不同的名称。”

    我解释说属性和方法的名称不同,但现在我猜你可能只是说 refA refB 和 refC 有不同的名称?

    如果允许您调整不同的实现,请为它们提供一个通用接口,并在整个代码中使用它,如其他答案中所述。否则适配器可能仍然是一种可行的方法,或者您可以为它们中的每一个创建一个包装类,并实现所需的接口。

    【讨论】:

      【解决方案4】:

      除非我误解了,否则您只需要一个界面。因此,只要 RefA-C 实现相同的属性和方法,就可以了:

      public interface IRef
      {
         string SomeProperty {get;}
         void SomeMethod();
      }
      

      然后实现RefA-C的接口:

      public class RefA : IRef
      {
          public string SomeProperty {get;}
      
          public void SomeMethod()
          {
             // Do for A
          }
      }
      
      public class RefB : IRef
      {
          public string SomeProperty {get;}
      
          public void SomeMethod()
          {
             // Do for B
          }
      }
      

      那么可以把接口称为实现的接口:

      public IRef LoadedClass;
      

      并将其实例化如下:

      if (UseConfigA) LoadedClass = new RefA(); // etc
      

      【讨论】:

      • will implement the same properties and methods but with different names。我相信你的答案是他所需要的,但我也会提到 explicit 接口实现。
      • @Snowbear - 我读到了,认为他的意思不同,一个叫 A,一个叫 B,一个叫 C...? 将实现相同的属性和方法
      • 根据问题陈述,我认为他不能更改引用的对象。
      • 理想情况下,您的答案是我想要的,但问题是每个属性和方法都有一个类独有的类型......我将在对我的原始帖子的编辑中进行解释。
      【解决方案5】:

      您确实需要一个接口。例如,考虑每个类都提供一个方法 doThis(),但 RefA 将其实现为 a(),RefB 将其实现为 b(),而 RefC 将其实现为 c()。你可以把接口看作抽象类,它不能提供任何代码,但一个类可以继承多个。

      你可以制作如下界面:

      interface CanDoThis{
          public void doThis();
      }
      

      然后你需要修改类文件如下:

      public class RefA : CanDoThis // this means "I implement the interface CanDoThis"
      {
          // Add this method, it is needed for the interface
          public void doThis(){
              a();
          }
      
          public void a(){
              // this has already been provided in the origional class file
          }
      }
      

      对于 RefB 和 RefC 也是如此。你的代码就变成了:

      public CanDoThis loadedClass;
      
      public Init()
      {
          /* load the object, according to which version we need... */
          if (Config.Version == "refA")
          {
              loadedClass = new RefA();
          }
          // etc
      }
      

      请注意,您将只能调用接口中定义的方法,因此您要调用的每个方法都需要在接口中定义。

      如果你的程序很好,你不应该需要它,但是如果你想检查实例属于哪个类,你可以像处理子类一样使用标准的“is”: if(loadedClass 是 RefA){ //... }

      【讨论】:

        【解决方案6】:

        我建议如果 3 种不同的类型共享一个公共接口,那么它们应该实现一个公共接口。例如IExample

        然后你可以从你的配置文件中加载正确的实现实例。

        // public fields are a no-no, use properties instead
        public IExample LoadedClass { get; private set; } 
        

        。 . .

        LoadedClass = (IExample)Activator.CreateInstance(Config.Version);
        

        Config.Version 是您班级的全名,例如命名空间.RefA

        【讨论】:

          【解决方案7】:

          我会这样做:

          1. 创建一个接口(我们称之为 ICommonStuff),该接口具有在 refA、refB 和 refC 之间共享的所有属性和方法。
          2. 对于 refA、refB 和 refC 中的每一个,创建一个接受类型作为参数并实现接口 ICommonStuff 的包装类。
          3. 在很大程度上实现您的 Init 方法,除了直接将 LoadedClass 属性设置为 refA、refB 或 refC,而是将其设置为包装器之一。

          这就是生成的代码:

          public ICommonStuff LoadedClass;
          
          public Init()
          {
              /* load the object, according to which version we need... */
              if (Config.Version == "refA")
              {
                  LoadedClass = new WrapperA(new refA());
              }
              else if (Config.Version == "refB")
              {
                  LoadedClass = new WrapperB(new refB());
              }
              else if (Config.Version == "refC")
              {
                  LoadedClass = new WrapperC(new refC());
              }
              Run();
          }
          private void Run(){
          {
              LoadedClass.SomeProperty...
              LoadedClass.SomeMethod(){ etc... }
          }
          

          【讨论】:

          • 这看起来很有希望,谢谢;我可能要到下周才能解决这个问题,但我会回来报告的。
          【解决方案8】:

          在共享的 .NET 程序集中声明接口。

          创建从接口继承的 A、B 和 C 类,并将它们放在不同的程序集中。

          在主项目中使用 Assembly.Load() 来加载您想要使用的程序集。在其中找到从接口继承的类并创建实例。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-27
            • 1970-01-01
            • 2022-01-27
            • 2023-03-26
            相关资源
            最近更新 更多