使用不同的兼容类型覆盖属性

发布于 2024-09-07 15:45:56 字数 670 浏览 3 评论 0原文

我需要一个具有属性的基类,在其中我可以派生具有相同属性但不同(兼容)类型的类。基类可以是抽象的。

public class Base
{
    public virtual object prop { get; set; }
}

public class StrBase : Base
{
    public override string prop { get; set; } // compiler error
}

public class UseIt
{
    public void use()
    {
        List<Base> l = new List<Base>();
        //...
    }
}

我尝试使用泛型,但这在使用该类时给我带来了问题,因为我想在列表中存储不同类型的基类。

public class BaseG<T>
{
    public T prop { get; set; }
}

public class UseIt
{
    public void use()
    {
        List<BaseG> l = new List<BaseG>(); // requires type argument
        //...
    }
}

I need a base class with a property where I can derive classes with the same property but different (compatible) types. The base Class can be abstract.

public class Base
{
    public virtual object prop { get; set; }
}

public class StrBase : Base
{
    public override string prop { get; set; } // compiler error
}

public class UseIt
{
    public void use()
    {
        List<Base> l = new List<Base>();
        //...
    }
}

I tried it with Generics but that gives me a problem when using the class, because I want to store differently typed base classes in the List.

public class BaseG<T>
{
    public T prop { get; set; }
}

public class UseIt
{
    public void use()
    {
        List<BaseG> l = new List<BaseG>(); // requires type argument
        //...
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

奢华的一滴泪 2024-09-14 15:45:56

这是建议的解决方案的另一种方法:

public abstract class Base
{
    public abstract void Use();
    public abstract object GetProp();
}

public abstract class GenericBase<T> : Base
{
    public T Prop { get; set; }

    public override object GetProp()
    {
        return Prop;
    }
}

public class StrBase : GenericBase<string>
{
    public override void Use()
    {
        Console.WriteLine("Using string: {0}", Prop);
    }
}

public class IntBase : GenericBase<int>
{
    public override void Use()
    {
        Console.WriteLine("Using int: {0}", Prop);
    }
}

基本上,我在中间添加了一个通用类,用于存储正确类型的属性。假设您永远不需要从迭代 List 成员的代码中访问 Prop,这将起作用。 (您始终可以向 Base 添加一个名为 GetProp 的抽象方法,如果需要,该方法将泛型强制转换为对象。)

示例用法:

class Program
{
    static void Main(string[] args)
    {
        List<Base> l = new List<Base>();

        l.Add(new StrBase {Prop = "foo"});
        l.Add(new IntBase {Prop = 42});

        Console.WriteLine("Using each item");
        foreach (var o in l)
        {
            o.Use();
        }
        Console.WriteLine("Done");
        Console.ReadKey();
    }
}

编辑:添加了 GetProp() 方法来说明如何从基类直接访问属性。

Here's an alternative approach to proposed solution:

public abstract class Base
{
    public abstract void Use();
    public abstract object GetProp();
}

public abstract class GenericBase<T> : Base
{
    public T Prop { get; set; }

    public override object GetProp()
    {
        return Prop;
    }
}

public class StrBase : GenericBase<string>
{
    public override void Use()
    {
        Console.WriteLine("Using string: {0}", Prop);
    }
}

public class IntBase : GenericBase<int>
{
    public override void Use()
    {
        Console.WriteLine("Using int: {0}", Prop);
    }
}

Basically I've added a generic class in the middle that stores your properly-typed property. this will work assuming that you never need to access Prop from the code that iterates the members of the List<Base>. (You could always add an abstract method to Base called GetProp that casts the generic to an object if that's required.)

Sample usage:

class Program
{
    static void Main(string[] args)
    {
        List<Base> l = new List<Base>();

        l.Add(new StrBase {Prop = "foo"});
        l.Add(new IntBase {Prop = 42});

        Console.WriteLine("Using each item");
        foreach (var o in l)
        {
            o.Use();
        }
        Console.WriteLine("Done");
        Console.ReadKey();
    }
}

Edit: Added the GetProp() method to illustrate how the property can be directly accessed from the base class.

烂人 2024-09-14 15:45:56

您无法覆盖属性的类型。看一下下面的代码:

StrBase s = new StrBase();
Base b = s;

这是完全有效的代码。但是当你尝试这样做时会发生什么?

b.prop = 5;

整数可以转换为object,因为一切都是从object派生的。但由于 b 实际上是一个 StrBase 实例,因此它必须以某种方式将整数转换为字符串,但它不能。这就是为什么您不允许覆盖该类型的原因。

同样的原则也适用于泛型:

List<BaseG<object>> l = new List<BaseG<object>>();
BaseG<string> s = new BaseG<string>();

// The compiler will not allow this.
l.add(s);

// Here's the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;

这是因为 C# 2.0 中的泛型类型是不变的。 C# 4.0 确实允许这种类型的转换,称为协变和逆变。

解决方案

一个选项是在需要时将对象强制转换回字符串。您可以在子类中添加类型验证:

public class StrBase : Base
{
  private string propValue;

  public override object prop {
    get
    {
      return this.propValue;
    }
    set
    {
      if (value is string)
      {
        this.propValue = (string)value;
      }
    }
  }
}

您还可以在子类中公开类型安全的属性:

public class StrBase : Base
{
  public string strProp {
    get
    {
      return (string)this.prop;
    }
    set
    {
      this.prop = value;
    }
  }
}

You can't override the type of a property. Take a look at the following code:

StrBase s = new StrBase();
Base b = s;

This is completely valid code. But what happens when you try to do this?

b.prop = 5;

The integer can be converted to object, because everything is derived from object. But since b is actually a StrBase instance, it would have to convert the integer to a string somehow, which it can't. So that is why you aren't allowed to override the type.

The same principle applies to generics:

List<BaseG<object>> l = new List<BaseG<object>>();
BaseG<string> s = new BaseG<string>();

// The compiler will not allow this.
l.add(s);

// Here's the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;

This is because generic types in C# 2.0 are invariant. C# 4.0 does allow this type of conversions, called covariance and contravariance.

Solutions

An option is to cast the object back to string when you need it. You could add type validation in the subclass:

public class StrBase : Base
{
  private string propValue;

  public override object prop {
    get
    {
      return this.propValue;
    }
    set
    {
      if (value is string)
      {
        this.propValue = (string)value;
      }
    }
  }
}

You could also expose a type-safe property in the subclass:

public class StrBase : Base
{
  public string strProp {
    get
    {
      return (string)this.prop;
    }
    set
    {
      this.prop = value;
    }
  }
}
墨小墨 2024-09-14 15:45:56

C# 9.0开始,这是可能的

从 C# 9.0 开始,重写方法支持协变返回类型。

(请参阅Microsoft 文档

This is possible since C# 9.0

Beginning with C# 9.0, override methods support covariant return types.

(see Microsoft docs)

难如初 2024-09-14 15:45:56
public class First
{
    private int someV;

    public virtual object SomeV { get => someV; set => someV = (int)value; }

    public First() { }
}

public class Two : First
{
    private string someV;

    public override object SomeV { get => someV; set => someV = value.ToString(); }

    public Two() { }
}

以及它们的使用:

First firstClass = new First();
firstClass.SomeV = 1;

Two twoClass = new Two();
twoClass.SomeV = "abcd";
public class First
{
    private int someV;

    public virtual object SomeV { get => someV; set => someV = (int)value; }

    public First() { }
}

public class Two : First
{
    private string someV;

    public override object SomeV { get => someV; set => someV = value.ToString(); }

    public Two() { }
}

and use of those:

First firstClass = new First();
firstClass.SomeV = 1;

Two twoClass = new Two();
twoClass.SomeV = "abcd";
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文