カテゴリー
C

独習C#第3版、P479 2つの型パラメータを持つジェネリック

namespace Chapter01All
{
    class TwoTypePrametorGeneric<T, V>
    {
        public V Kasan(T a, V b)
        {
            return (V)( (dynamic)a + b);
        }
    }
    class IntToDouble : TwoTypePrametorGeneric<int, double> { }
    class DoubleToInt : TwoTypePrametorGeneric<double, int> { }

    class Demo
    {
        static void Main()
        {
            IntToDouble objA = new IntToDouble();
            Console.WriteLine(objA.Kasan(100, 100.5));  //200.5

            DoubleToInt objB = new DoubleToInt();
            Console.WriteLine(objB.Kasan(100.5, 200));  //300
        }
    }
}

例えばの話ですが、intとdoubleを加算しようと思えば、キャストすればいいのですが、それと同じような雰囲気のクラスを作って見ました。クラスの名称と関数を使うときに、対応は間違えられないとは思います。

カテゴリー
C

C#、ジェック型クラスと型パラメータの制約、基本クラス制約

using System;

class Chapter01All
{
    class GenericCalcBase<T>
    {
        public T Kasan(T a, T b)
        { 
            return (dynamic)a + (dynamic)b;
        }
        public T Genzan(T a, T b)
        {
            return (dynamic)a - (dynamic)b;
        }
    }
    class MyCalcClassInt : GenericCalcBase<int> { }
    class MyCalcClassDouble : GenericCalcBase<double> { }
    class Test<T> where T : MyCalcClassInt
    {
        T obj;
        public Test(T o = null)
        {
            obj = o;
        }
    }
    class DelivedTest : GenericCalcBase<int>{ }
    static void Main()
    {
        GenericCalcBase<int> objA = new GenericCalcBase<int>();
        Console.WriteLine(objA.Kasan(100, 200));        //300

        MyCalcClassInt objB = new MyCalcClassInt();
        Console.WriteLine(objB.Kasan(500, 300));        //800

        MyCalcClassInt objC = new MyCalcClassInt();
     //  Console.WriteLine(objC.Kasan(1000.5, 2000));  //1000.5にするとエラーになる

        DelivedTest objE = new DelivedTest();
        Console.WriteLine(objE.Kasan(1001, 2002));      //3003

        Test<MyCalcClassInt> objD = new Test<MyCalcClassInt>(objB);
        Console.WriteLine(objD);    //objDからobjBにアクセスは出来ないようです。

        MyCalcClassDouble objF = new MyCalcClassDouble();
        Console.WriteLine(objF.Kasan(1.234, 10.234));   //11.468
        Console.WriteLine(objF.Genzan(1.234, 10.234));  //-9
    }
}

何となく少し掴めたかもしれません。

カテゴリー
C

C# 型パラメータと継承が混乱してます。

using System;

class Chapter01All
{
    class GenericSample
    {
       public int Kasan(int a, int b)
       { 
            return a + b;
       }
    }
    class MyClassInt : GenericSample { }
    class Test<T> where T : MyClassInt
    {
        T obj;
        public Test(T o = null)
        {
            obj = o;
        }
    }
    class DelivedTest : GenericSample
    {
    }
    static void Main()
    {
        GenericSample objA = new GenericSample();
        Console.WriteLine(objA.Kasan(100, 200));        //300

        MyClassInt objB = new MyClassInt();
        Console.WriteLine(objB.Kasan(500, 300));        //800

        MyClassInt objC = new MyClassInt();
        Console.WriteLine(objC.Kasan(1000, 2000));      //3000

        DelivedTest objE = new DelivedTest();
        Console.WriteLine(objE.Kasan(1001, 2002));      //3003

        Test<MyClassInt> objD = new Test<MyClassInt>(objB);
        Console.WriteLine(objD);    //objDからobjBにアクセスは出来ないようです。
    }
}

自分の場合は、継承の方が今のとこわかり易い感じがします。型パラメータの制約はスッキリしません。只記号:を使うので似ている感じもしますが、違うという意識を持った方が良い?のかもしれません。Mainの最後の行で、結構悩みました。デバッグ画面からだと、objDからobjBに辿れたように思います。

カテゴリー
C

独習C#第3版、P482 ジェネリック 基本クラス制約を使用する

using System;

class Chapter01All
{
    class GenericSample<T>
    {
        public T Kasan(T a, T b)
        {
            a = (dynamic)a + (dynamic)b;
            return a;
        }
        public T Genzan(T a, T b)
        {
            a =(dynamic)a - (dynamic)b;
            return a;
        }
        public T Kakezan(T a, T b)
        {
            a = (dynamic)a * (dynamic)b;
            return a;
        }
        public T Warizan(T a,  T b)
        {
            a =(dynamic)a / (dynamic)b;
            return a;
        }
    }
    class MyClassInt : GenericSample <int>{ }
    class MyClassDouble : GenericSample<double> { }
    class MyClass : GenericSample<int> { }
    class Test<T> where T : MyClass
    {
        T obj;
        public Test( T o)
        {
            obj = o;
        }
    }
    

    
    static void Main()
    {
        GenericSample<int> obj = new GenericSample<int>();
        int a = 100; int b = 200;
        Console.WriteLine(obj.Kasan(a,  b));        //300

        GenericSample<double> objA = new GenericSample<double>();
        double c = 100.01; double d = 200.02;
        Console.WriteLine(objA.Kakezan(c, d));      //20004.0002

        MyClassInt objB = new MyClassInt();
        Console.WriteLine(objB.Genzan(500, 400));   //100

        MyClassDouble objD = new MyClassDouble();
        Console.WriteLine(objD.Warizan(200.2, c));  //2.00179982 

        MyClass objE = new MyClass();
        Console.WriteLine(objE.Kasan(100, 200));    //300

        Test<MyClass> t1 = new Test<MyClass>(objE); 
        Console.WriteLine(t1);    //Chaper01All+Test'1[Chapter01All+MyClass]
    
    }
}

GenericSampleクラスは、ジェネリッククラスでint型とdouble型を取ります。実行のコードの最後の部分が理解できなくて、途中半端になってますが、実行自体はできます。t1インスタンからKasanを実行する方法が出来ないのか?暫く悩んでます。

カテゴリー
C

独習C#第3版、P374 例外を投げる、取り敢えずやって見ました

using System;

// FailSoftArrayにLengthプロパティを追加する
namespace Chapter01All
{
    class RangeOverException : ApplicationException
    {
        public RangeOverException() : base() { }
        public RangeOverException(string msg) : base(msg) { }
        public override string ToString()
        {
            return "\n" + Message;
        }
    }
    class RangeUnderException : ApplicationException
    {
        public RangeUnderException() : base() { }
        public RangeUnderException(string msg) : base(msg) { }
        public override string ToString()
        {
            return "\n" + Message;
        }
    }
    class FailSoftArray
    {
        int[] a; // 配列への参照
        int len; // 配列の長さ

        public static bool ErrFlag; // 直前の操作の結果を表すフラグ

        // サイズを指定して配列を作る
        public FailSoftArray(int size)
        {
            a = new int[size];
            len = size;
        }

        // 読み取り専用プロパティLength
        public int Length
        { //←Lengthを、フィールドからプロパティに変更する
            get
            {
                return len;
            }
        }

        // FailSoftArrayオブジェクトのためのインデクサ
        public int this[int index]
        {
            // getアクセサ
            get
            {
                if (ok(index))
                {
                    ErrFlag = false;
                    return a[index];
                }
                else
                {
                    ErrFlag = true;
                    return -1;
                }
            }

            // setアクセサ
            set
            {
                if (ok(index))
                {
                    a[index] = value;
                    ErrFlag = false;
                }
                else ErrFlag = true;
            }
        }

        // インデックスが配列の上限・下限の範囲内ならtrueを返す
        private bool ok(int index)
        {
            if (index >= 0 & index < Length) return true;
            if (index < 0) throw new RangeUnderException("range under");
            throw new RangeOverException("range over");
            //return false;
        }
    }

    // 改良されたフェイルソフト配列
    class ImprovedFSDemo
    {
        static void Main()
        {
            FailSoftArray fs = new FailSoftArray(5);
            int x;

            // Lengthを読み取ることができる
            for (int i = 0; i < (fs.Length); i++)
                fs[i] = i * 10;

            for (int i = 0; i < (fs.Length); i++)   //i<=(fs.Length)として見るとエラーを捉えますが...
            {                                       // int i= -1;でもエラーを捉えますが...
                x = fs[i];
                if (x != -1) Console.Write(x + " ");
            }
            Console.WriteLine();

            //fs.Length = 10; // 不正、コンパイルエラー!
        }
    }
}

 throwで例外を捕えましたが、行きっぱなしでした。自分でとらえたほかに、システムでも捉えました。自分でとらえた時はエラーで終了では、上手くないですね。またやって見る必要ありそうです。

カテゴリー
C

独習C#第3版、P262 FailSoftArray インデクサ

// インデクサを追加して、フェイルソフトな配列を改良する
using System;
namespace Chapter07All
{
    class FailSoftArray
    {
        int[] a; // 配列への参照

        public static int Length; // 変数Lengthはpublic staticに変えてみました。

        public static bool ErrFlag; // 直前の操作の結果を表すフラグ 同じくstaticに変えてみました。

        // サイズを指定して配列を作る
        public FailSoftArray(int size)
        {
            a = new int[size];
            Length = size;
        }

        // FailSoftArrayオブジェクトのためのインデクサ
        public int this[int index]
        { //←FailSoftArrayに対するインデクサ
          // getアクセサ
            get
            {
                if (ok(index))
                {
                    ErrFlag = false;
                    return a[index];
                }
                else
                {
                    ErrFlag = true;
                    return -1;
                }
            }

            // setアクセサ
            set
            {
                if (ok(index))
                {
                    a[index] = value;
                    ErrFlag = false;
                }
                else ErrFlag = true;
            }
        }

        // インデックスが配列の上限・下限の範囲内ならtrueを返す
        private bool ok(int index)
        {
            if (index >= 0 & index < Length) return true;
            return false;
        }
    }

    // 改良されたフェイルソフトな配列
    class ImprovedFSDemo
    {
        public static void Main()
        {
            FailSoftArray fs = new FailSoftArray(5);
            int x;

            // 失敗を表示しない
            Console.WriteLine("Fail quietly.");
            for (int i = 0; i < (FailSoftArray.Length * 2); i++) //クラス変数のアクセスはクラス名で修飾 
                fs[i] = i * 10; //←インデクサのsetアクセサが呼び出される

            for (int i = 0; i < (FailSoftArray.Length * 2); i++)
            {
                x = fs[i];    //←インデクサのgetアクセサが呼び出される
                if (x != -1) Console.Write(x + " ");
            }
            Console.WriteLine();

            // 今度はエラーを表示
            Console.WriteLine("\nFail with error reports.");
            for (int i = 0; i < (FailSoftArray.Length * 2); i++)
            {
                fs[i] = i * 10;
                if (FailSoftArray.ErrFlag)
                    Console.WriteLine("fs[" + i + "] out-of-bounds");
            }

            for (int i = 0; i < (FailSoftArray.Length * 2); i++)
            {
                x = fs[i];
                if (!FailSoftArray.ErrFlag) Console.Write(x + " ");
                else
                    Console.WriteLine("fs[" + i + "] out-of-bounds");
            }
        }
    }
}

 本の説明を見ると、LengthとErrFlagはメンバ変数で無くて、クラス変数の様だったので、staticにしてみました。正常そうには動きました。アクセス方法が違ってきます。インスタン変数であればfs.Lengthとなるのですが、クラス変数の場合はFailSoftArray.Lengthと変わります。

カテゴリー
C

独習C#第3版、P508 ジェネリック対応のキューを作成する、ジェネリックインターフェイスを付けてみました

using System;

/*
   例5-2

   文字型データ用のQueueクラス
*/

interface IFSample<T>
{
    void Put(T obj);
    T Get();
}
class SimpleQueue<T> : IFSample<T>
{
    public T[] q; // キューのデータを保持する配列
    public int putloc, getloc; // put操作やget操作用の添え字

    public SimpleQueue(int size) { 
        q = new T[size + 1]; // キュー用にメモリを確保する
        putloc = getloc = 0;
    }

    // 1文字分だけキューに追加する
    public void Put(T obj) {
        if (putloc == q.Length - 1)
        {
            Console.WriteLine(" -- Queue is full.");
            return;
        }

        putloc++;
        q[putloc] = obj;
    }

    // キューから1文字分だけ取り出す
    public T Get()
    {
        if (getloc == putloc)
        {
            Console.WriteLine(" -- Queue is empty.");
            throw new Exception();
        }
        getloc++;
        return q[getloc];
    }
}

// Queueクラスを使う
class QDemo
{
    static void Main()
    {
        SimpleQueue<char> bigQ = new SimpleQueue<char>(100);
        SimpleQueue<int> smallQ = new SimpleQueue<int>(5);
        char ch;
        int i;

        Console.WriteLine("Using bigQ to store the alphabet.");
        // bigQにいくつかの文字を追加する
        for (i = 0; i < 26; i++)
            bigQ.Put((char)('A' + i));

        // bigQから取り出して表示する
        Console.Write("Contents of bigQ: ");
        for (i = 0; i < 26; i++)
        {
            ch = bigQ.Get();
            if (ch != (char)0) Console.Write(ch);
        }

        Console.WriteLine("\n");

        Console.WriteLine("Using smallQ to store integer.");

        // smallQを使ってエラーを起こさせる
        for (i = 0; i < 5; i++)
        {
            Console.Write("Attempting to store " + i);
            smallQ.Put(i);
            Console.WriteLine();
        }
        Console.WriteLine();

        // さらにsmallQでエラーを起こさせる
        Console.Write("Contents of smallQ: ");
        int g;
        for (i = 0; i < 5; i++)
        {
            g = smallQ.Get();

            Console.Write(g);
        }
    }
}

 この場合、インターフェイスはクラスのプロトタイプみたいなものですが、自分的にはあとから、こうも書けるみたいな感じが好きなので、前のものを直してみました。

カテゴリー
C

独習C#第3版、P508 ジェネリック対応のキューを作る

using System;

/*
   例5-2

   文字型データ用のQueueクラス
*/

class SimpleQueue<T>
{
    public T[] q; // キューのデータを保持する配列
    public int putloc, getloc; // put操作やget操作用の添え字

    public SimpleQueue(int size) { 
        q = new T[size + 1]; // キュー用にメモリを確保する
        putloc = getloc = 0;
    }

    // 1文字分だけキューに追加する
    public void Put(T obj) {
        if (putloc == q.Length - 1)
        {
            Console.WriteLine(" -- Queue is full.");
            return;
        }

        putloc++;
        q[putloc] = obj;
    }

    // キューから1文字分だけ取り出す
    public T Get()
    {
        if (getloc == putloc)
        {
            Console.WriteLine(" -- Queue is empty.");
            throw new Exception();
        }
        getloc++;
        return q[getloc];
    }
}

// Queueクラスを使う
class QDemo
{
    static void Main()
    {
        SimpleQueue<char> bigQ = new SimpleQueue<char>(100);
        SimpleQueue<int> smallQ = new SimpleQueue<int>(5);
        char ch;
        int i;

        Console.WriteLine("Using bigQ to store the alphabet.");
        // bigQにいくつかの文字を追加する
        for (i = 0; i < 26; i++)
            bigQ.Put((char)('A' + i));

        // bigQから取り出して表示する
        Console.Write("Contents of bigQ: ");
        for (i = 0; i < 26; i++)
        {
            ch = bigQ.Get();
            if (ch != (char)0) Console.Write(ch);
        }

        Console.WriteLine("\n");

        Console.WriteLine("Using smallQ to store integer.");

        // smallQを使ってエラーを起こさせる
        for (i = 0; i < 5; i++)
        {
            Console.Write("Attempting to store " + i);
            smallQ.Put(i);
            Console.WriteLine();
        }
        Console.WriteLine();

        // さらにsmallQでエラーを起こさせる
        Console.Write("Contents of smallQ: ");
        int g;
        for (i = 0; i < 5; i++)
        {
            g = smallQ.Get();

            Console.Write(g);
        }
    }
}

本では、エラー処理含めての例題になってますが、自分はジェック型クラスの取り扱い方のみで、例題を参考にして打ち込んでみました。何度かやって見ないと分からないですね。Getではqueueが空の場合、throw new Exception();してます。リターン値があるのですが、出来ない場合もあります。throwするとreturnが無くても良いようです。

カテゴリー
C

独習C#第3版、P485 型パラメータの制約 二つの型パラメータの関係を確立する制約

using System;

namespace Chapter01All
{
    class A
    {
        public A()
        {
            Console.WriteLine("A constractor");
        }
        public void OutPutsA()
        {
            Console.WriteLine("A");
        }
    }
    class B : A
    {
        public B()
        {
            Console.WriteLine("B constractor");
        }
        public void OutPutsB()
        {
            Console.WriteLine("B");
        }

    }
    class MyGenClass<T,V> where V : T
    {
        public MyGenClass()
        {
            Console.WriteLine("MyGenClass constractor");
        }
        public void OutPutsMyGenClass()
        {
            Console.WriteLine("MyGenClass");
        }
    }

    class NakedConstraintDemo
    {
        static void Main()
        {
            MyGenClass<A, B> x = new MyGenClass<A, B>();
            x.OutPutsMyGenClass();
            
            B objB = new B();
            objB.OutPutsA();
            objB.OutPutsB();

            A ojbA = new A();
            ojbA.OutPutsA();
     
        }
    }
}

 意味が分かりません。実行の画面では、MyGenClassのインスタンスxではメンバー関数のOutPutsMyGenClass()だけ実行できました。その意味ではAのインスタンスobjAと同様です。コンストラクタも同じです。BクラスのインスタンスobjBはコンストラクタは2個呼ばれますし、OutPutsA()とOutPutsB()の両方実行できます。objBの方が普通の説明ですよね。

inserted by FC2 system