关于c#:如何在不进行转换的情况下引用具有不同类型的父变量?

How to reference parent variable with a different type without casting?

我有这种情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Base
{
    public Basedef def;
}

public class A : Base
{
}

public class B : A
{
    public int GetBar()
    {
        return def.bar;
    }
}


public class BaseDef
{
}

public class ADef : BaseDef
{
    public int foo;
}

public class BDef : ADef
{
    public int bar;
}

如您所见,方法b:getbar()中有一个错误,因为def没有访问bar的权限,但是如果您使…

1
2
3
4
public int GetBar()
{
    return (def as BDef).bar;
}

应该可以,但我想避免强制转换,如何使用在基类中创建的引用从定义中获取属性而不使用强制转换?

为什么要避免演员阵容?,因为很容易出现运行时错误并且更容易引入错误,所以我需要类型安全的编码。

我想做的

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Factory
{
    public static Base<BaseDef> Create(BaseDef d)
    {
        if(d is BDef)
            return new B(); //Error, can not convert B to Base<BaseDef>
    }
}

public class Program
{
    B instance = Factory.Create(new BDef()); //Error, can not convert to Base<BaseDef> to B
}

我在找一个优雅的解决方案

再见!

  • 我想你希望ADEF从basedef继承,但是你没有写它,所以将basedef强制转换成bdef是没有意义的…
  • 你为什么要避免演员阵容?没有虚拟方法或属性/反射,您试图实现的目标是不可能实现的。
  • 我已经编辑了你的问题,假设你是指这个ADef : BaseDef。如果不编辑你的问题
  • 斯里拉姆,是的,我是说,谢谢你指出
  • sriram,更新了问题,补充了为什么我要避免铸造


为了有一个优雅的、无强制转换的解决方案,编译器需要知道defGetBar()中的BDef。这是一种方法,我认为这对您的情况很有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Base<T> where T : BaseDef
{
    public T def { get; set; }
}

public class A<T> : Base<T> where T : ADef
{
    public int GetFoo()
    {
        return def.foo; // this works, too
    }
}

public class B : A<BDef>
{
    public int GetBar()
    {
        return def.bar;
    }
}

(顺便说一下,你应该使用公共财产,而不是公共领域。老实说,公共变量和公共属性访问器有什么区别?出于某些原因。)

更新:您的Factory方法可能如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static Base<T> Create<T>(T d) where T : BaseDef
{
    if(typeof(T) == typeof(BDef))
        return (Base<T>)(object)new B();
    else
        return null;
}
public static T Create<T, U>(U d) where T : Base<U> where U : BaseDef
{
    T result;
    if (typeof(T) == typeof(B))
        result = (T)(object)new B();
    else
        throw new NotImplementedException();
    result.def = d;
    return result;
}
public static T CreateAlternate<T, U>(U d) where T : Base<U>, new() where U : BaseDef
{
    return new T { def = d };
}

用途:

1
2
3
4
5
6
void Main()
{
    Factory.Create(new BDef());
    Factory.Create<B, BDef>(new BDef());
    Factory.CreateAlternate<B, BDef>(new BDef());
}

我喜欢最后一个,因为没有强制转换,只要new()约束不是问题,或者第一个(如果简洁的调用代码是非常有价值的)约束(因为可以推断出泛型类型)。

  • 我考虑过这个问题,a可以使用其def引用获取foo值吗?
  • @JoeCabezas是的,我的解决方案支持这一点,因为编译器知道defADef
  • 这给了我错误:baseb=new a();(无法隐式转换类型A' to base')
  • 因为同样的原因,Collection c = new ObservableCollection();是无效的:它根本没有意义。你想怎么用这个?您可能会从Base中受益,它是Base的一个基类。如果您只需要Tinout,那么您可以在通用接口上使用co/contravariance,但就像您有getset一样,这样就不起作用。
  • 在我的问题"我想做什么"中加了一句,如果你有时间,请看一下。
  • @JoeCabezas我为您添加了一些示例Factory方法。

  • Why avoid cast?, because is prone to runtime errors and is easier to introduce bugs, I want type-safe coding.

    我不明白为什么Cast容易出错,如果你不知道defRuntimeType是什么,我会说你的设计是错误的。

    在我看来,你应该知道什么是它的运行时类型。

    有解决办法

    工作1:

    1
    2
    3
    4
    5
    6
    7
    public int GetBar()
    {
        if (def is BDef)
            return ((BDef)def).bar;

        return 0;//some default value
    }

    工作2:介绍一个enum,说who am I

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public enum DefType
    {
        BaseDef = 0,
        ADef =1,
        BDef =2
    }

    public class BaseDef
    {
        public virtual DefType MyType
        {  
            get{ return  DefType.BaseDef; }
        }
    }

    public class ADef
    {
        public override DefType MyType
        {  
            get{ return  DefType.ADef; }
        }
    }

    然后像这样使用

    1
    2
    3
    4
    5
    6
    7
    8
    switch(def.MyType)
    {
        case DefType.ADef:
        {
            (ADef).foo;//you know who is this here right?
        }
        ...
    }


    用演员表做这件事是不安全的,因为你要做的基本上是不安全的。

    您有一个类A和一个类B,它们是base的子类,base引用了basedef。basedef可以是adef或bdef,你不知道是哪一个,当然不是从任何告诉b的东西。

    但是,如果使用泛型,可以向B提供需要知道其basedef引用实际上是bdef的信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Base<T> where T : BaseDef
    {
        public T def;
    }

    public class A<T> : Base<T> where T : ADef
    {
    }

    public class B : A<BDef>
    {
        public int GetBar()
        {
            return def.bar;
        }
    }

    将basedef设置为abstact类并在其上禁止抽象属性是否有意义?

    • 不,因为bar是b的一个特定属性,所以我们的想法是使用def类来创建a和b的实例。
    • 此外,A不应使用自己的DEF访问BAR。