C# Collection of Different Generic Types Without Casting

时间:2018-04-18 18:16:04

标签: c#

I've found couple of questions on the same topic here, however I couldn't find what I need. Basically I am searching for this kind of magic:

public class BaseClass
{
    public int DerivedТype { get; set; }
}

public class DerivedClass<T> : BaseClass
{
    public DerivedClass(T initialValue)
    {
        DerivedТype = 1;
        Property = initialValue;
    }

    public T Property { get; set; }
}

public class OtherDerivedClass<T> : BaseClass
{
    public OtherDerivedClass(T initialValue)
    {
        DerivedТype = 2;
        OtherProperty = initialValue;
    }

    public T OtherProperty { get; set; }
    public int OtherProperty2 { get; set; }
    public float OtherProperty { get; set; }
}

public class Program
{
    public static void Main()
    {
        List<BaseClass> baseClassList = new List<BaseClass>();
        baseClassList.Add(new DerivedClass<int>(5));
        baseClassList.Add(new OtherDerivedClass<float>(6));
        foreach (var derived in baseClassList)
        {
            if (derived.DerivedТype == 1)
            {
                Console.WriteLine(derived.Property);
            }
            else if (derived.DerivedТype == 2)
            {
                Console.WriteLine(derived.OtherProperty);
            }
        }
    }
}

I want a list of BaseClass where I can insert instances of DerivedClass and OtherDerivedClass. So far so good.

DerivedClass and OtherDerivedClass hold different properties so I really have no idea how access them. Also I don't want to use any weired casts. So this part of the code prevents me from building.

if (derived.DerivedТype == 1)
{
    Console.WriteLine(derived.Property);
}
else if (derived.DerivedТype == 2)
{
    Console.WriteLine(derived.OtherProperty);
}

Any ideas would be appreciated. Thank you in advance!

4 个答案:

答案 0 :(得分:1)

这看起来像是一个可以用多态解决的问题。我会为您的应用制作一个与您在示例中完全相同的应用版本,但如果有更多关于您的目标目标的信息,则解决方案可能会有所不同。

public abstract class BaseClass
{
    public abstract void DoSomething();
    public abstract void GetData(Dictionary<string,string> container);
}

public class DerivedClass<T> : BaseClass
{
    public DerivedClass(T initialValue)
    {
        Property = initialValue;
    }

    public T Property { get; set; }
    public override void DoSomething()
    {
        Console.WriteLine(Property);
    }

    public override void GetData(Dictionary<string,string> container)
    {
        container.Add(nameof(Property), "{Property}");
    }
}

public class OtherDerivedClass<T> : BaseClass
{
    public OtherDerivedClass(T initialValue)
    {
        OtherProperty = initialValue;
    }

    public T OtherProperty { get; set; }
    public int OtherProperty2 { get; set; }

    public override void DoSomething()
    {
        Console.WriteLine(OtherProperty);
    }

    public override void GetData(Dictionary<string,string> container)
    {
        container.Add(nameof(OtherProperty), "{OtherProperty}");
        container.Add(nameof(OtherProperty2), "{OtherProperty2}");
    }
}

你的foreach循环可以简单如下:

foreach(var derived in baseClassList) derived.DoSomething();

这是使用OO执行此类操作的正确方法。不需要DerivedType整数,因为对象知道它是什么类型以及做什么。这就是人们使用多态的原因。它简单而优雅,面向对象。将DoSomething扩展或更改为更适合您尝试做的事情。

OP提出了他自己的解决方案,但如果目标是对更有意义的数据做一些事情,你也可以将一个对象传递给一个允许你这样做的抽象方法。我添加了一个GetData方法,它将所有属性值作为字符串返回。第二种类型的字典也可以是object,实际值存储在字典中。

BaseClass也可以是一个常规类,其中包含一个方法,用字符串键返回object值的IDictionary。该方法可以使用反射来获取它所基于的任何类的所有属性值。但是,反射有更多的开销,因此从执行的角度来看,这不是最有效的方法。

检查对象是否属于某种类型的正确方法是使用is运算符,例如:

if(derived is DerivedType<int>)
{
    // Do what you need to do with the specific object type
}

如果你知道你要投射这个物体,正如阿多西所指出的那样,你会使用:

var castedValue = derived as DerivedType<int>;
if(castedValue != null)
{
    // Do what you need to do with castedValue
}

如果对象不是DerivedType<int>类型,则返回null。尝试使用(DerivedType)派生会导致无效的强制转换异常。

答案 1 :(得分:0)

如果您不介意使用运行时类型,可以使用dynamic

例如:

List<BaseClass> baseClassList = new List<BaseClass>();
baseClassList.Add(new DerivedClass<int>(5));
baseClassList.Add(new OtherDerivedClass<float>(6));

// Here use "dynamic"
foreach (dynamic derived in baseClassList)
{
    if (derived.DerivedТype == 1)
    {
        Console.WriteLine(derived.Property);
    }
    else if (derived.DerivedТype == 2)
    {
        Console.WriteLine(derived.OtherProperty);
    }
}

答案 2 :(得分:0)

据我所知,你想要的是不可能而不是一个好主意。 Typechecking在编译时完成。像Dynamic这样的东西可以将这些检查移动到运行时,但它会导致各种问题(带动态参数的函数也会返回动态)。

如果你至少得到了C#7.0,你至少可以为它编写一个开关。旧开关仅支持几个选择值类型和字符串的值与常量。但C#7.0引入了pattern matching。有了它,您甚至可以使用is检查作为案例的一部分。

答案 3 :(得分:0)

谢谢大家的支持!我决定简单地使用演员。

public class BaseClass
{
    public int DataТype { get; set; }

    public object Data { get; set; }
}

public class DataClass<T>
{
    public DataClass(T initialValue)
    {
        Property = initialValue;
    }
    public T Property { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        List<BaseClass> listBaseClass = new List<BaseClass>();
        BaseClass dummy = new BaseClass();
        dummy.DataТype = 1;
        dummy.Data = new DataClass<int>(50);
        listBaseClass.Add(dummy);
        if (listBaseClass[0].DataТype == 1)
        {
            DataClass<int> casted = (DataClass<int>)listBaseClass[0].Data;     
            Console.WriteLine(casted.Property);
        }      
    }
}