【问题标题】:Need something like static inheritance in C#在 C# 中需要像静态继承这样的东西
【发布时间】:2011-05-07 15:30:20
【问题描述】:

我遇到了一个小设计问题,想咨询一下。 假设我们有以下类层次结构:

abstract class A
{
}

class B : A
{
}

class C: A
{
}

我希望 B 和 C 都有一个特定的字段 x,这样它的值在类之间是不同的,但在同一类的所有实例之间共享(即:如果 b1、b2 是 B 的实例,而 c1、c2 是C 然后 b1.x = b2.x 和 c1.x = c2.x 和 b1.x != c1.x)。 有没有一种优雅的方法可以利用 B、C 都从同一个基类派生的事实来做到这一点,还是我必须在两个类中创建一个静态字段 x?

提前致谢。

【问题讨论】:

  • 小修正,当然我的意思是 b1.x != c1.x.
  • 这是您自己的问题,您应该可以在没有任何声誉要求的情况下对其进行编辑。
  • 你当然是对的,我错过了编辑标签,对不起。
  • 不用道歉,你是新来的。

标签: c# inheritance static


【解决方案1】:

你的意思是这样的?

abstract class A
{
    static Dictionary<Type, int> all_x;
    protected int X {
        get { return all_x[GetType()]; }
        set { all_x[GetType()] = value; }
    }
}

如果它必须是一个字段,那么您可以通过引用传递:

abstract class A
{
    class SharedType { int x; }
    static Dictionary<Type, SharedType> all_shared;
    protected SharedType Shared {
        get
        {
            Type t = GetType();
            SharedType result;
            if (!all_shared.TryGetValue(t, out result) {
                 result = new SharedType();
                 all_shared.Add(t, result);
            }
            return result;
        }
    }
}

此外,我们可以通过对每个实例只进行一次查找来提高性能:

abstract class A
{
    class SharedType { int x; }
    static Dictionary<Type, SharedType> all_shared;
    protected SharedType Shared;
    A() {
        Type t = GetType();
        if (!all_shared.TryGetValue(t, out Shared) {
             Shared = new SharedType();
             all_shared.Add(t, Shared);
        }
    }
}

【讨论】:

  • 我想到了那个选项,但我希望有更好的选择。谢谢。
  • @Marina:你不喜欢它什么?有一些技巧可以用来让它更快(平均而言),或者拥有一个可以通过引用传递的真实“字段”,但它们也会增加复杂性。
  • @Victor:那你是我读问题的方式不同,因为我的理解是@Marina 希望BD 拥有x 的单独副本,因为它们是不同的类.但是这个问题有很多模棱两可的地方。也许是D的成员函数应该使用xdB的成员函数应该使用xb,而不考虑对象的动态类型。在@Marina 澄清之前无法判断。
  • 如果你想要速度,那么你应该在每个类中编写一小段代码,让它编译器满意。真的没有那么多。而且我想我前段时间在这里看到了完全相同的问题..
  • @Euphoric:如果你的意思是在两个类中创建一个静态字段,那将不会达到我所理解的预期效果......你甚至不能参考 b1.x 和 @ 987654332@ 更不用说让a =&gt; a.x 返回不同的结果,具体取决于传入的是b1 还是c1。但我对这个问题的解释可能是错误的。
【解决方案2】:

字段 x 的这些值应该是什么?如果您需要指定 A 的 x 值应为“a”,B 的 x 值应为“b”等,那么您必须指定值“a”、“b”、.. . 在某个地方,然后你最好只使用:

abstract class A {
    public static int x = 1;    // Just using "int" as example.
}

class B : A {
    public static int x = 2;
}

如果您不关心值是什么(那么您需要哪种类型),而只是希望值“不同”,那么您可以使用以下内容而不是使用字段:

abstract class A {
    public int X { get { return this.GetType().GetHashCode(); } }
}

这并没有考虑哈希冲突,但也许它还是有用的?

你想要达到什么目的?

【讨论】:

    【解决方案3】:

    以 Ben Voigt 的第一个答案为基础,我认为您想要的基类是这样的:

    public abstract class A
        {
            private static ConcurrentDictionary<Type, int> _typeIDs = new ConcurrentDictionary<Type, int>();
            private static int _nextID = 1;
    
            public int TypeID
            {
                get
                {
                    return _typeIDs.GetOrAdd(this.GetType(), type => System.Threading.Interlocked.Increment(ref _nextID));
                }
            }
        }
    

    【讨论】:

      【解决方案4】:
      public abstract class A
      {
          public abstract int Value { get; }
      }
      
      public class B : A
      {
          public override int Value { get { return 1; } }
      }
      
      public class C : A
      {
          public override int Value { get { return 2; } }
      }
      

      【讨论】:

      • 如果此作者的字段具有不变的值(并且她没有在原始问题中指定),那么这将用于为每个类返回一个不同的值。
      • -1。 autor 不想要常量。而且他只想在 A 类中解决它。所以 B 和 C 中没有额外的代码。
      【解决方案5】:

      我知道的唯一方法是让 A 类成为泛型类,即 A&lt;T&gt; 类。然后让 B 类为泛型类型实现与 C 类实现的泛型类型不同的类型。

      如果你不使用泛型,那么我相信这在 .NET 中是不可能的。

      这是一个示例,假设您感兴趣的值是具有成员 int Foo 和字符串 Bar 的数据结构。一个派生类可以实现与另一个相同的结构(但派生类型不同) - 这两个结构将实现相同的接口。

      interface IAvalue
          {
              int Foo { get; set;}
              string Bar {get; set;}
          }
      
          struct BValue
              : IAvalue
          {
      
              public int Foo { get; set; }
              public string Bar { get; set; }
          }
      
          struct CValue
              : IAvalue
          {
              public int Foo { get; set; }
              public string Bar { get; set; }
      
      
          }
      
          abstract class A<T> where T : IAvalue
          {
              protected static T myValue;
          }
      
          class B : A<BValue>
          {
              static B()
              {
                  myValue.Foo = 1;
                  myValue.Bar = "text1";
              }
          }
      
          class C : A<CValue>
          {
              static C()
              {
                  myValue.Foo = 2;
                  myValue.Bar = "text2";
              }
          }
      

      【讨论】:

      • 这使每个类的 myValue (或作者问题中的“x”)具有不同的数据类型,而不仅仅是不同的值。一个副作用是值会有所不同,但我不认为作者打算使用两种不同的数据类型,即使 int 和 long 之间存在一些兼容性。
      • 我编辑了我的答案,可能更容易接受 - 共享相同的界面
      • 它现在更加抽象了一点,仍然是改变基本字段的数据类型的相同概念。现在通过共享数据类型(接口)实现隐式兼容性。
      • 正如@Euphoric 在别处指出的那样,作者表示她想要基类中的分辨率:Is there an elegant way to do this by taking advantage of the fact that both B, C derive from the same base class 当作者创建接口时,支持结构并另外为每个派生类添加泛型,它有点违背了目的。在这种情况下,她还不如简单地在每个派生类上放置一个静态字段来实现相同的目的。
      【解决方案6】:

      您可以使用一个 .net 功能:如果您在泛型类中有静态数据成员,.net 会为您使用的每种泛型类型创建不同的静态数据成员实例。

      所以,你可以写:

      public abstract class A<T> where T : A<T>
      {
          protected static int myVariable { get; set; }
      }
      

      并将您的类继承为:

      public class B : A<B>
      {
          public B()
          {
              myVariable = 1;
          }
          public int GetVariable()
          {
              return myVariable;
          }
      }
      
      public class C : A<C>
      {
          public C()
          {
              myVariable = 2;
          }
          public int GetVariable()
          {
              return myVariable;
          }
      }
      

      然后B 的每个实例将共享对myVariable 实例的访问权,而C 的每个实例将共享对另一个实例的访问权。

      所以,如果你添加Set(int a)方法:

      public void Set(int a)
      {
          myVariable = a;
      }
      

      并运行以下代码:

      static void Main(string[] args)
      {
          B b1 = new B();
          C c1 = new C();
          B b2 = new B();
          C c2 = new C();
      
          Console.Write("{0}; ", b1.GetVariable());  // 1
          Console.Write("{0}; ", b2.GetVariable());  // 1
          Console.Write("{0}; ", c1.GetVariable());  // 2
          Console.Write("{0}; ", c2.GetVariable());  // 2
      
          Console.WriteLine();
      
          c2.Set(333);
      
          Console.Write("{0}; ", b1.GetVariable());  // 1
          Console.Write("{0}; ", b2.GetVariable());  // 1
          Console.Write("{0}; ", c1.GetVariable());  // 333
          Console.Write("{0}; ", c2.GetVariable());  // 333
      
          Console.ReadLine();
      }
      

      你会得到:1; 1; 2; 2; 1; 1; 333; 333; 输出。

      【讨论】:

        【解决方案7】:

        我建议定义一个静态 Dictionary,并让基类构造函数调用 GetType() 并查看它是否还在静态字典中。如果没有,则创建一个新的单元素数组并将其存储在字典中。否则从字典中获取数组并将其存储在实例字段中。然后定义一个读取或写入数组元素零的属性。这种方法将为类的所有衍生物和子衍生物实现所请求的语义。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-18
          • 1970-01-01
          • 2012-06-12
          相关资源
          最近更新 更多