【问题标题】:C# Generic Base PolymorphismC# 泛型基多态
【发布时间】:2017-05-14 07:18:13
【问题描述】:

我有一个通用基类和两个从基类派生的类。我想用对象工厂创建这种派生类。但我不能用通用基类做到这一点。我可以用这种方式在 Java 中毫无问题地做到这一点。我还在 c# 中通过装箱和拆箱解决了这个问题。这样做的正确方法是什么?

public abstract class Base<T> {
    public abstract T Value {
        get;
        set;
    }
}

public class Derived1 : Base<int> {
    int value;

    public override int Value {
        get {
            return value;
        }
        set {
            this.value = value;
        }
    }
}

public class Derived2 : Base<float> {
    float value;

    public override float Value {
        get {
            return value;
        }
        set {
            this.value = value;
        }
    }
}

public class Factory {
    public Base create(int type) {
        switch(type) {
            case 1:
                return new Derived1();
            case 2:
                return new Derived2();
        }
    }
}

谢谢!

已解决:我知道,由于任何接口都没有通用方法,因此它们也无济于事。唯一的解决方案是将值装箱到对象中。这是我的解决方案。

public abstract class Base {
    public abstract object Value {
        get;
        set;
    }
}

public class Derived1 : Base {
    int value;

    public override object Value {
        get {
            return value;
        }
        set {
            this.value = (int) value;
        }
    }
}

public class Derived2 : Base {
    float value;

    public override object Value {
        get {
            return value;
        }
        set {
            this.value = (float) value;
        }
    }
}

public class Factory {
    public Base create(int type) {
        switch(type) {
            case 1:
                return new Derived1();
            case 2:
                return new Derived2();
            default:
                return null;
        }
    }
}

现在我可以将所有派生对象视为基础对象。

【问题讨论】:

  • 我有点迷路了。你那里有什么问题?
  • 只要您不介意在为Factory 添加支持的类型时继续添加int 值分配,这就足够了。 ...除了 @PeterA.Schneider 指出的那样,您必须返回非通用 Base&lt;T&gt;
  • @TyCobb 我猜你不能这样使用Base;您必须在 Base&lt;int&gt;Base&lt;float&gt; 之间做出选择,哪种方式会破坏目的。
  • 解决方案当然是使Factory.create() 通用。 (是的,你现在说的,@TyCobb。)
  • @PeterA.Schneider Awww 我错过了工厂中的Base 签名。泛型必须传播到create 调用

标签: c# generics inheritance polymorphism


【解决方案1】:

你必须有一个非泛型的Base&lt;T&gt;返回类型; 你可以考虑使用TypeCode:

public class Factory {
    public Base<T> create<T>() {
        var typeCode = Type.GetTypeCode(typeof(T));
        switch (typeCode)
        {
            case TypeCode.Empty:
                break;
            case TypeCode.Object:
                break;
            case TypeCode.DBNull:
                break;
            case TypeCode.Boolean:
                break;
            case TypeCode.Char:
                break;
            case TypeCode.SByte:
                break;
            case TypeCode.Byte:
                break;
            case TypeCode.Int16:
                break;
            case TypeCode.UInt16:
                break;
            case TypeCode.Int32:  // Derived1 : Base<int>
                return new Derived1();

                break;
            case TypeCode.UInt32:
                break;
            case TypeCode.Int64:
                break;
            case TypeCode.UInt64:
                break;
            case TypeCode.Single:  // Derived2 : Base<float>
                return new Derived2();

                break;
            case TypeCode.Double:
                break;
            case TypeCode.Decimal:
                break;
            case TypeCode.DateTime:
                break;
            case TypeCode.String:
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}

...另外,如果您使用了接口,您可以“隐藏”&lt;TypeParam&gt; 从返回:

public interface IBase {
    // properties/methods
}

然后:

public abstract class Base<T> : IBase {
        // properties/methods
}

然后:

public class Factory {
    public IBase create<T>() {
        // logic
    }
}

现在,对于返回Value,有三种选择:(1) 坚持不使用接口IBase 的实现; (2)在接口中使用dynamic返回类型,或者; (3) object 中的值,就像你所做的那样。

注意:在我的头顶上写下来,未经测试。

// 1. implementation without the interface    
public abstract class Base<T> {
    public abstract T Value {get; set;}
}

// 2. implementation with IBase
public interface IBase {
    public dynamic Value {get; set;}
}

public abstract Base<T> : IBase {
    public abstract dynamic Value {get; set;}
}

【讨论】:

  • 对 TypeCode 的使用很好,而不是大量的 if(typeof(T)... 检查。 +1
  • 这里使用接口也是一种更简洁的方法。也许如果我回家后变得不懒惰,我会添加它。
  • @IAbstract 是的,我会很感激的。可能我解释的不好。工厂之后,我想访问每个对象的 Value 属性,无论其类型如何。例如:Base d1 = new Factory().Create(1); d1.Value 应该返回整数值。
  • @e.imer 这只适用于动态语言。由于 C# 是静态类型的,因此它必须知道使用它时值的类型。您要么需要强制转换为 int,然后将类型全部忽略并使用 object,要么从头到尾使用泛型。
【解决方案2】:

这是实现您想要的一种方法,但工厂的调用者需要知道正在创建的类型。我还将派生类中的属性更改为自动属性,因为您没有对它们进行任何特殊处理,但如果您愿意,可以将它们改回来。

public abstract class Base<T> {
   public abstract T Value
   {
      get;
      set;
   }
}

public class Derived1 : Base<int> {
   public override int Value { get; set; }
}

public class Derived2 : Base<float> {
   public override float Value { get; set; }
}

public class Factory {
   public Base<T> Create<T>(int type) {
      switch( type ) {
         case 1:
         return new Derived1() as Base<T>;
         case 2:
         return new Derived2() as Base<T>;
      }

      return null; // or whatever you want to do
   }
}

用法如下:

var f = new Factory();
var one = f.Create<int>( 1 );
var two = f.Create<float>( 2 );

就像我提到的,工厂的调用者需要一些关于正在创建的类型的知识,因为这会失败:

var f = new Factory();
var one = f.Create<int>( 2 );   // <-------- Changed from 1 to 2
var two = f.Create<float>( 1 ); // <-------- Changed from 2 to 1

你总是可以从工厂抛出一个异常来解释为什么你不能创建请求的类型,以便调用者可以采取适当的步骤。

【讨论】:

  • 可能我解释的不好。在工厂之后,我想访问每个对象的 Value 属性,无论其类型如何。例如:Base d1 = new Factory().Create(1); d1.Value 应该返回整数值。
猜你喜欢
  • 2018-06-07
  • 1970-01-01
  • 1970-01-01
  • 2012-12-10
  • 1970-01-01
  • 2017-03-28
  • 2017-08-20
  • 2012-01-20
  • 2010-12-21
相关资源
最近更新 更多