如何订购2个不同的清单?

时间:2013-01-08 15:00:31

标签: c#

我有一个Step班级和一个AutoStep班级

class Step {
    int StepNumber;
}

class AutoStep {
    int StepNumber;
}

他们从任何类继承而 CAN NOT 从任何类继承。

我必须按StepNumber对它们进行排序,并根据其类型调用特定方法(StepAutoStep

我该怎么做?

6 个答案:

答案 0 :(得分:5)

这就是为什么接口:

public interface ISteppable
{
    public int StepNumber { get; }
    public void Foo();//the method you need to call; adjust the signature as needed
}

class step : ISteppable
{
    int StepNumber;

    int ISteppable.StepNumber
    {
        get { return StepNumber; }
    }

    public void Foo()
    {

    }
}

class AutoStep
{
    int StepNumber;

    int ISteppable.StepNumber
    {
        get { return StepNumber; }
    }

    public void Foo()
    {

    }
}

然后,您可以创建ISteppable个对象的集合,并使用其StepNumber属性对其进行排序,并在每个对象上调用Foo

由于您无法修改任何类,因此您需要使用适配器模式为这些类型创建这些接口的实现:

public class StepAdapter : ISteppable
{
    private step value;
    public StepAdapter(step value)
    {
        this.value = value;
    }

    public int StepNumber
    {
        get { return value.StepNumber; }
    }

    public void Foo()
    {
        value.Foo();
    }
}

public class AutoStepAdapter : ISteppable
{
    private AutoStep value;
    public AutoStepAdapter(AutoStep value)
    {
        this.value = value;
    }

    public int StepNumber
    {
        get { return value.StepNumber; }
    }

    public void Foo()
    {
        value.Foo();
    }
}

然后,您可以创建ISteppable个对象的集合,当您想要添加step对象时,只需将其包装在StepAdapter中并包装所有AutoStep个对象在AutoStepAdapter个对象中。

List<ISteppable> list = new List<ISteppable>();

list.Add(new StepAdapter(new step(){StepNumber = 5}));
list.Add(new AutoStepAdapter(new AutoStep(){StepNumber = 3}));

list.Sort((a, b) => a.StepNumber.CompareTo(b.StepNumber));

foreach (var item in list)
{
    item.Foo();
}

答案 1 :(得分:3)

您可以使用as投射它们:

Step stepObject = input as Step;

if(stepObject != null)
{
  // do something with Step
}

AutoStep autoStepObject = input as AutoStep;

if(autoStepObject != null)
{
  // do something with AutoStep
}

答案 2 :(得分:1)

如果你想要以同样的方式对待两种类型,你应该以某种方式为它们创建一种类型。

在这种情况下,最好的选择是一个通用接口(或者可能是一个公共基类),但这似乎不适合你。

另一种方法是创建接口和从中继承的两个适配器(每个原始类型一个)。

这两个选项都在Servy's answer中解释清楚。

但另一种选择是使用dynamic。有了这个,拥有一个具有相同名称的属性就足以以相同的方式使用它们。但我不推荐这种方法,因为它打破了(编译时)类型安全性。

例如(使用Servy答案的修改代码):

List<dynamic> list = new List<dynamic>();

list.Add(new step{ StepNumber = 5 });
list.Add(new AutoStep{ StepNumber = 3 });

foreach (var item in list.OrderBy(x => x.StepNumber))
{
    item.Foo();
}

答案 3 :(得分:0)

如果您可以使用界面,问题很容易解决:

class step : IStep
{
   ...
   public int StepNumber {get; set;}
}

class AutoStep : IStep
{
   ...
   public int StepNumber {get; set;}
}

interface IStep
{
   public int StepNumber;
}

List<IStep> steps; // list with steps and autosteps
List<IStep> orderedSteps = steps.OrderBy(s => s.StepNumber);
foreach (IStep step in orderedSteps)
{
  if (step is Step)
    // call to method here
  else if (step is AutoStep)
    // call to other method here
}

或者你可以用Reflection做一些事情(不建议由于性能影响)。

答案 4 :(得分:0)

如果没有严格的约束,我绝对不会做任何事情,但你可以使用以下的排序方法:

private void sort(object toBeSorted)
{
    if(toBeSorted is Step)
    {
        callStepSort();
    }
    else if(toBeSorted is AutoStep)
    {
        callAutoStepSort();
    }
}

它负责检查类型,避免继承,并允许您调用sort方法而不管其类型。

但是,再次,没有限制,这根本不是我将如何实现这一目标。

答案 5 :(得分:0)

假设您定义了两个列表:

List<AutoStep> autoStepList;
List<Step> stepList;

根据您的课程定义,字段StepNumber是非公开的,因此我们需要通过Reflection访问它。

您可以将以下类添加到项目中:

using System.Collections.Generic;
using System.Reflection;
using System.Linq;

public partial class AnyStepComparer<T>: IComparer<T> {
    int IComparer<T>.Compare(T stepX, T stepY) {
        if(typeof(Step)==typeof(T)||typeof(AutoStep)==typeof(T))
            return (
                from it in new[] { 0 }
                let type=typeof(T)
                let flagsAccess=BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public
                let invokeAttr=flagsAccess|BindingFlags.GetProperty|BindingFlags.GetField
                let binder=default(Binder)
                let args=default(object[])
                let x=(int)type.InvokeMember("StepNumber", invokeAttr, binder, stepX, args)
                let y=(int)type.InvokeMember("StepNumber", invokeAttr, binder, stepY, args)
                select Comparer<int>.Default.Compare(x, y)
                ).First();

        var message="{0} must be either Step or AutoStep";
        var paramName="[T]";
        throw new ArgumentException(String.Format(message, paramName), paramName);
    }

    public static readonly AnyStepComparer<T> Default=new AnyStepComparer<T>();
}

然后在代码中定义方法:

public static void SortSteps<T>(List<T> steps) {
    if(steps is List<AutoStep>||steps is List<Step>)
        steps.Sort(AnyStepComparer<T>.Default);
}

这样你就可以通过调用它们来对它们进行排序:

SortSteps(autoStepList);

SortSteps(stepList);

或者,您可以使用以下命令直接在代码中对它们进行排序:

autoStepList.Sort(AnyStepComparer<AutoStep>.Default);

stepList.Sort(AnyStepComparer<Step>.Default);