Step
クラスとクラスAutoStep
があります
class Step {
int StepNumber;
}
class AutoStep {
int StepNumber;
}
それらはどのクラスからも継承されず、どのクラスからも継承できません。
StepNumberで並べ替え、type(Step
またはAutoStep
)に応じて特定のメソッドを呼び出す必要があります
どうやってやるの?
Step
クラスとクラスAutoStep
があります
class Step {
int StepNumber;
}
class AutoStep {
int StepNumber;
}
それらはどのクラスからも継承されず、どのクラスからも継承できません。
StepNumberで並べ替え、type(Step
またはAutoStep
)に応じて特定のメソッドを呼び出す必要があります
どうやってやるの?
これは文字通り、インターフェイスが作成されたものです。
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();
}
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 つの型を作成する必要があります。
この場合の最良のオプションは、共通のインターフェイス (または、おそらく共通の基本クラス) ですが、それは選択肢ではないようです。
もう 1 つの方法は、インターフェイスと、それを継承する 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();
}
次の 2 つのリストを定義したとします。
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);
厳密に制約されずに物事を行う方法ではありませんが、次のような並べ替えメソッドを使用できます。
private void sort(object toBeSorted)
{
if(toBeSorted is Step)
{
callStepSort();
}
else if(toBeSorted is AutoStep)
{
callAutoStepSort();
}
}
これにより、型のチェックが行われ、継承が回避され、型に関係なく sort メソッドを呼び出すことができます。
しかし、繰り返しになりますが、制約がなければ、これは私がこれを達成する方法ではありません。
インターフェイスを使用できる場合、問題はかなり簡単に解決できます。
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 を使用して何かを行うこともできます (パフォーマンスへの影響があるため、お勧めしません)。