- PR -

型定義のみが異なる複数のクラス(C#)

投稿者投稿内容
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2003-07-22 00:41
型定義のみが異なり制御ロジックが同じ複数のクラスをスマートに表現する方法は
ありますか?
1.同じ制御ロジックを複数箇所に書くのは保守面で良くない。
2.いくつかの型については処理効率を重視するため厳密に区別したい。
この2つの条件を両立させる方法を知りたいと思っています。
本当はもっと複雑なのですがわかりやすくするために下記の例をあげます。

using System;

namespace test
{

// 質問
// 制御ロジックは同一で変数の型だけが異なる2つのクラスがあった場合
// 同じ制御ロジックを2つ記述するのは保守の面で問題があります。
// 制御ロジックを1つにするスマートな記述方法はあるでしょうか?
// 前提
// double型のbufferでbyte型とdouble型の両方を格納すれば解決できますが、
// 今回の趣旨としては無しです。

public class ClassStatistics
{
// バイト型のクラス
// bufferはbyte[]型でsum_xはint型で定義する
public class ClassByteSigma
{
private byte[] buffer;
private int number;
private int sum_x;

public ClassByteSigma()
{
buffer = new byte[1000];
number = 0;
sum_x = 0;
}
public void Add(byte x)
{
sum_x += x;
number++;
}
public int Number()
{
return number;
}
public double Average()
{
return (double)sum_x/number;
}
}

// 倍精度型のクラス
// bufferはdouble[]型でsum_xはdouble型で定義する
public class ClassDoubleSigma
{
private double[] buffer;
private int number;
private double sum_x;

public ClassDoubleSigma()
{
buffer = new double[1000];
number = 0;
sum_x = 0;
}
public void Add(double x)
{
sum_x += x;
number++;
}
public int Number()
{
return number;
}
public double Average()
{
return sum_x/number;
}
}
}
}
なな
ぬし
会議室デビュー日: 2003/06/22
投稿数: 659
お住まい・勤務地: 愛知県
投稿日時: 2003-07-22 08:09
C++ならば、templateってところでしょうが...

独自interfaceを定義して、そのinterfaceからbyte型とdouble型の型を定義してみてはいかがでしょう?

MyInterface -+- MyByte
|
+- MyDouble
小野@どっとねっとふぁん
ぬし
会議室デビュー日: 2001/10/30
投稿数: 402
投稿日時: 2003-07-22 12:36
次のメジャーバージョンアップの際に取り込まれることになっているジェネリクスに期待、ってとこでしょうか。。。

http://www.microsoft.com/japan/msdn/vs/vcsharp/vbconCProgrammingLanguageFutureFeatures.asp
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2003-07-22 17:21
ご返答ありがとうございます。未だインターフェースを使ったことがないので、
ちょっと勉強してみます。(C#)


引用:

ななさんの書き込み (2003-07-22 08:09) より:
C++ならば、templateってところでしょうが...

独自interfaceを定義して、そのinterfaceからbyte型とdouble型の型を定義してみてはいかがでしょう?

MyInterface -+- MyByte
|
+- MyDouble


ishisaka
常連さん
会議室デビュー日: 2001/10/10
投稿数: 23
投稿日時: 2003-07-22 18:52
 伊藤さんのサンプルよりももう少し複雑な場合で、アルゴリズムと、実装の関係を設計的にすっきりさせたいのであればtemplate methodパターン(デザインパターン)を使うところだと思います。
 後は、あまりやりたくないのですが、オブジェクト型の引数と戻り値を持つメソッドを作ってしまって、そのメソッドを使う側で戻り値をキャストさせる方法もあります。(まぁこれが大変問題のある方法だというのはわかります。でもGenericsサポートまではこんな方法で逃げるしかないのかもしれません。)
_________________
いしさかただひろ(*^^)v
mei
大ベテラン
会議室デビュー日: 2003/04/08
投稿数: 114
投稿日時: 2003-07-22 22:56
こんばんは、meiです。

CodeDomを使った遊び。
※注、Managed C# Compilerへの参照が必要です。

// ひな形ソースから、int型クラスとdouble型クラスを作ってみる例
using System;
using System.CodeDom.Compiler;
using System.Reflection;

public class ArrayTemplate {
// テンプレートコード
private const string code = @"
using System;
public class XXXArray {
private XXX[] buffer;
private XXX sum_x;
private int number;

public XXXArray() {
buffer = new XXX[1000];
number = 0;
sum_x = 0;
}
public void Add(XXX x) {
buffer[number++] = x;
sum_x += x;
}
public int Number() {
return number;
}
public double Average() {
return (double)sum_x/number;
}
}";
public static object Create(Type type) {
// ソースコードのXXXをtype型に置換
string src = code.Replace("XXX", type.Name);

// メモリ上でコンパイル
ICodeCompiler cc = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.AddRange(new string[] {"mscorlib.dll","System.dll"});
cp.GenerateInMemory = true;
CompilerResults cr = cc.CompileAssemblyFromSource(cp, src);
if(cr.Errors.HasErrors) {
return null;
}
// インスタンスを返す
return cr.CompiledAssembly.CreateInstance(type.Name + "Array");
}

}

// メインクラス
class MyApp {
static void ArrayTest(object arr, params object[] args) {
MethodInfo add = arr.GetType().GetMethod("Add");
MethodInfo num = arr.GetType().GetMethod("Number");
MethodInfo ave = arr.GetType().GetMethod("Average");

foreach (object o in args)
add.Invoke(arr, new object[] {o});

Console.WriteLine("Number : " + num.Invoke(arr, null));
Console.WriteLine("Average : " + ave.Invoke(arr, null));
}

static void Main(string[] args) {
// int型のArrayクラスを生成
object iArr = ArrayTemplate.Create(typeof(int));
// double型のArrayクラスを生成
object dArr = ArrayTemplate.Create(typeof(double));

// テスト
Console.WriteLine(iArr.GetType().Name);
ArrayTest(iArr, 1,1,2,3,5);
Console.WriteLine(dArr.GetType().Name);
ArrayTest(dArr, 1.1,2.2,3.3,4.4);
}
}

面倒なので実際には使えないけど(笑)

cats
大ベテラン
会議室デビュー日: 2002/11/29
投稿数: 221
お住まい・勤務地: 東京
投稿日時: 2003-07-23 08:35
ArrayTemplateすごいですね。

ふと、(データをコードとして扱える)Lispを思い出しました。
cats
大ベテラン
会議室デビュー日: 2002/11/29
投稿数: 221
お住まい・勤務地: 東京
投稿日時: 2003-07-23 09:56
簡単な例(Max)でいろいろ作ってみました。
コード:
using System;
using System.Reflection;

public class Test 
{
	// Max2を利用する場合は、IComparableは不要
	struct MyInt : IComparable
	{
		int		val;
		public MyInt(int i) {val = i;}
		public override string ToString() {return val.ToString();}
		public int CompareTo(object o)
		{
			return val - ((MyInt)o).val;
		}
	}
	// インターフェースによる方法
	public static IComparable Max1(IComparable i1,IComparable i2)
	{
		return i1.CompareTo(i2) > 0 ? i1 : i2;
	}
	// リフレクションによる方法
	public static object Max2(object o1,object o2)
	{
		Type t = o1.GetType();
		MethodInfo mi = t.GetMethod("CompareTo");
		return (int)mi.Invoke(o1,new object[]{o2}) > 0 ? o1 : o2;
	}
	// delegateによる方法
	public delegate int CompareToDelegate(object o);
	public static object Max3(CompareToDelegate d1,object o2)
	{
		return d1(o2) > 0 ? d1.Target : o2;
	}
	public static void Main(string[] args)
	{
		MyInt m12 = new MyInt(12), m34 = new MyInt(34);
		Console.WriteLine(Max1(12,34));
		Console.WriteLine(Max1(m12,m34));
		Console.WriteLine(Max2(12,34));
		Console.WriteLine(Max2(m12,m34));
		Console.WriteLine(Max3(new CompareToDelegate(12.CompareTo),34));
		Console.WriteLine(Max3(new CompareToDelegate(m12.CompareTo),m34));
	}
}


Max3は美しくないな。

スキルアップ/キャリアアップ(JOB@IT)