0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

クラスとメソッド (六週目の学習)

Last updated at Posted at 2018-10-26

モチベーションも大事だと思うので覚書として使用します。

六週目の感想
六週目は、基本のクラスとメソッドをより詳しく学習。
インデクサが不安だったけど、色々な記事でコードを見るうち理解してきてC#7の対応文も書いて、
少し自信がついたと思います。
MSDNの華氏記録は℃や色々な表現からの変換も加えたら面白そうだなーと思いました。

#クラスとメソッド

##引数

渡し返される/渡され返す(result/return)

MSDN:基本的なメソッドの構文
基本、以下の形になる。1つのクラスにMainメソッドの他、AとBメソッドが記述してある例。
7~9行目はMainメソッド内からAメソッドの手続きを2回、Bメソッドの手続きを1回実行。

1:namespace MySapce1
2:{
3: public class MyApp
4: {
5: public static void Main()
6: {
7: A(); //A の実行
8: B(); //B の実行
9: A(); //A の実行
10: }
11: public static void A()
12: {
13: // 任意の手続きの記述
14: }
15: public static void B()
16: {
17: // 任意の手続きの記述
18: }
19: }
20:}

処理結果である戻り値を扱うためには、どんな引数かあらかじめ定義しておく必要がある。
この引数 x、y はローカルな変数として機能し、x と y は自由に利用できる。
メソッドの呼び出し元へ処理結果のデータを返すには、return w;

public static int Add(int x, int y)
{
int w; // 処理データ返す用のreturn w定義
w = x + y;// 処理データ返す用のreturn w代入
return w;// 処理データ返すreturn w. 型は冒頭で定義したintなるのでなくてよい
}

もし、このメソッドで戻り値を扱わないのならば、戻り値型の代わりに void をつける。

次に定義した引数分、値を渡し返してもらう必要がある(3行目result = Add(x, 10);として渡し、返される)
returnで渡され返すresultで渡し受け取る.結果result=110

1: int x=30;
2: int result;
3: result = Add(x, 10);

以下全文

1:namespace MySpace
2:{
3: public class MyApp
4: {
5: public static void Main()// メインメソッド
6: {
7: int x=10;// 定義
8: int result;// 定義
9: result = Add(x, 100);// ここで渡し返される
10: System.Console.WriteLine("result=" + result);
11: }
12: public static int Add(int a, int b)// 引数ありのメソッド定義
13: {
14: int w;// 定義
15: w = a + b;// 代入
16: return w;// ここで渡され返す
17: }
18: }
19:}

注意点!値型は実体コピーだが、参照型は仮引数側も実引数側も配列実体や文字列実体からコピーしているにすぎない
public void accum(int a, int [] dat, string str) 値型、配列型、文字列型 3つの引数定義
{
System.Console.WriteLine("a=" + a);
System.Console.WriteLine("dat[0]=" + dat[0]);
System.Console.WriteLine("str=" + str);
}

☝参照渡しができる(ref)
refを使用してメソッドパラメータ(値型または参照型のいずれか)を渡す場合、 実引数の変更が仮引数にもコピーでなく反映される。

c-sharpcorner:ref

public class Reff01// 仮引数GetData(ref int a)と呼出時GetData(ref a)
{
    static void Main(string[] args)//メインメソッド
    {
        int a = 4;
        Console.WriteLine($"Value before method call is: {a}");// 4(一番目に処理)
        GetData(ref a);//refで参照渡し
        Console.WriteLine($"Value after method call is: {a}");// 4/ refで892(三番目に処理)
        Console.ReadKey();
    }

    public static void GetData(ref int a)//仮引数 //refで参照渡し
    {
        a = a + 888;//仮引数定義
        Console.WriteLine($"Value inside method is: {a}");// 892(二番目に処理)
        // 100行目の呼出でついでに実行された↑
        // refで参照渡しにした事で仮引数も更新され1003になる(refなければ4のまま)
    }
}

void MAin()      {result=Add(渡したい引数).実行文} 下記処理返される
int Add(int a, int b) {return w;} 渡されて計算結果返す
void MAin()       {GetData(ref a)}下記処理される
void GetData(ref int a)refで更新された値(refなければMainのint aの定義のまま)

##再帰関数

x = n * method(階乗の場合n-1);
コード例(階乗n!…5! = 5 × 4 × 3 × 2 × 1 = 120)
//fact = n * CalcFact(n - 1);で呼び出す必要がある

namespace ClassAndMethod
{
    class FactClass
    {
        public long CalcFact(int n)
        {
            long fact;// long64のfactという変数 後にこの変数はelseで条件にされた

            if (n == 0)
            {
                fact = 1;
            }
            else
            {
                fact = n * CalcFact(n - 1);// このメソッド内の条件内で階乗用に再帰呼び出し
            }
            return fact;
            
        }
    }

    class Fact01
    {
        public static void Main()
        {
            FactClass f = new FactClass();
            for (int i = 0; i <= 20; i++)
                Console.WriteLine($"{i}!={f.CalcFact(i)}");
        }
    }
}

/*
最初はint=の初期化子で0の階乗、反復子i++で1の階乗(i <= 20までループ)
例えば5に到達した場合、n!=f.CalcFact(i)は、5!=f.CalcFact(4)となる
f.CalcFactは17行目のn*CalcFact(n-1)
分解すると、        5*CalcFact(4)

重要なのは、CalcFactは関数なので計算用の値でなく呼出用の値
FactClassに属するfを定義したので、ドット演算子を用いてf.CalcFact(fはCalcFactMethod呼び出す奴宣言)
呼び出されたこのメソッドは具体的に、一番上に定義されたlong fact;(long64のfact)を表す
さらに、if文にこのfactの値はn*CalcFact(n-1)と条件にされたので、
5*CalcFact(4)が成立した。(そしてi++で20まで階乗され続けた)
 */



自らのメソッド自身を呼び出し階乗計算できる

##ref,out,in

ref,out
コード例(s.Swap(ref x, ref y))
class Ref01
{
    private int temp;

    public void Swap(ref int x,ref int y)
    {
        temp = x;
        x = y;
        y = temp;
    }
}

class Ref02
{
    public static void Main()
    {
        Ref01 s = new Ref01();
        int x = 10, y = 20;// refは渡す前に値代入必須
        s.Swap(ref x, ref y);
        Console.WriteLine($"x={x}, y={y}");// x=20, y=10
    }
}
コード例(out)
//p.GetCoordinates(out var x1, out int y1);// 1行で済む

static void testOutVariables(string str)
{
   // 変数 n をメソッド呼び出し時に宣言できる。
   if (Int32.TryParse(str, out int n))
   {
       Console.WriteLine(n);
   }
   else
   {
       Console.WriteLine("整数に変換できません。");
   }
}


//例2
class Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public void GetCoordinate(out int x, out int y)// 参照元
    {
        x = X;
        y = Y;
    }
}

class Program
{
    static void Main()
    {
        var p = new Point { X = 1, Y = 2 };
        p.GetCoordinate(out var x, out var y);

     // ローカル変数宣言内(=呼出元)はvarもok

        // 以下のような書き方をしたのと同じ
        // int x, y;
        // p.GetCoordinate(out x, out y);

        Console.WriteLine($"{x}, {y}");
    }
}


returnや再帰した先で変更してもコピーに過ぎないので参照元のメソッドに変化なし
→refやoutは更新される、または元のメソッド内のオブジェクトに変更加えれば更新される

※一方outはrefと違い宣言だけで使えるため便利
(しかも引数リスト内でも宣言ok,ローカル変数宣言内(=呼出元)はvarもok)

out
        Console.Write("age : ");// 年齢

        //int nInput;// outの受け皿 ※C#7から同時宣言できるようなった
        
// int.TryParseはもし成否が、成功の場合は変換結果がnInputへ格納される
        if (int.TryParse(Console.ReadLine(), out nInput))
            { t.Age = nInput; }// ←intなら代入 できないなら文字列なのでelse↓

        else
        { Console.Write("! please retype of number");
            if (int.TryParse(Console.ReadLine(), out var nInput))C#7から同時宣言できるようなった
                t.Age = nInput;
        }
        // ↑ intのTryParseで参照渡しokならt.Ageに代入 これ用にnInput定義
        // ↓ while(10-98以外ループ) 

        while (t.Age < 9 || t.Age > 99)
            {
                Console.Write("! retype age: ");
                if (int.TryParse(Console.ReadLine(), out nInput))
                    t.Age = nInput;
            }
out2

//`int number;`// outの受け皿

// int.TryParseは結果として成否を返すが、成功の場合は変換結果がnumberへ格納される
`bool result = int.TryParse("1234", out var number)`

`result=true number=1234`

##dot演算子

MyNamespace.SampleClass.myMethod();
コード例
using System;

namespace MyNamespace
{
    public class SampleClass
    {
        public static void myMethod()
        {
            Console.WriteLine("Creating my namespace");
        }
    }
}
 
namespace MyProgram
{
    public class MyClass
    {
        public static void Main()
        {
            MyNamespace.SampleClass.myMethod();
        }
    }
}

// Creating my namespace

別階層のネームスペース名 クラス名 メソッド名(MyMethod();)を呼び出せる

##ローカル変数の参照渡しreturns

Ref Returns メソッドの戻り値を参照として返す  Ref Locals 値を参照として受ける

これまでref 引数 (参照渡し)があったがメンバ変数としてだった。
C#7からローカル変数として参照渡しが可能となる(ref returns ref locals)

コード例(弾く事で省く &&より||)
class RefReturns
{
    static void Main()
    {
        var a = new int[] { 0, 1, 2, 3, 4 };// 配列 int[]
        Console.WriteLine(string.Join(",", a));// 連結 string.join("区切",a)
        
        ref var d = ref GetValue(a);  // 参照返しを変数 d で受ける
        d = 5;                        // 書き換え 
        Console.WriteLine(string.Join(",", a));
        // 0, 1, 5, 3, 4
    }

    static ref int GetValue(int[] b)// intの配列型b
    {
        ref var c = ref b[2];  // b の 3 番目の要素を参照する変数 c を作る
                               // 引数の値確保(intは引数で定義すれば以降無しOK)
        
        return ref c;          //--- 変数 c の参照先を返す
    }
}

最初のメソッドがref dはaで、dは5
次のメソッドで ref cはb[2]
refでcをaに参照返し。つまりc=aと同じなのでd=cが成立
結果3番目が5になり、0,1,5,3,4

ls>

コード例(ユーザ入力で変動的 &&より&)

class Main method ref(渡し受ける)メンバ変数(クラスField内)
method ref(受け返す)ローカル変数(メソッドField内のみ).
これにより上refが下refと同じ意味になる(記述はrefのみで判断)

##メソッドのオーバーロード

some public int~(引数){式} class~ Main(){new dot}

引数前にparams

コード例(引数自動判別)

namespace OverLoadMethod
{
    class Test01
    {
        public string getCount(int a)// string型のメソッド
        {
            return a + "回目";
            // return $"{a} 回目"
        }

        public string getCount(int a, string b)// 仮引数違うオーバーロードメソッド
        {
            return a + "回目" + b;
            // return $"{a} 回目 {b}";
        }
    }
    class Test02
    {
        static void Main()// メイン
        {
            Test01 t1 = new Test01();// new演算子で渡す用にt1定義

            string msg1 = t1.getCount(2);// ドット演算子でt1のgetCountに(2)渡し
                // 2はintなので引数自動判別でint a
            Console.WriteLine(msg1); //2回目

            string msg2 = t1.getCount(2, "です");// ;同じく(2,"です")渡し
                // 2,"です"はintとStringなので引数自動判別でint a, string b
            Console.WriteLine(msg2); //2回目です
        }
    }
}
コード例(サフィックスや実引数の数も判定基準)
class Method_overloading
{
    public int Addition(int a, int b)
    {
        int x;
        return x = a + b;
    }
    public int Addition(int a, int b, int c)
    {
        int y;
        return y = a + b + c;
    }
    public float Addition(float a, float b)
    {
        float u;
        return u = a + b;
    }
    public float Addition(float a, float b, float c)
    {
        float v;
        return v = a + b + c;
    }
}
//Now you can use those Addition method four types  
class hub
{
    public static void Main(String[] args)
    {
        Method_overloading mthover = new Method_overloading();// new演算子で渡す用にmthover定義
        Console.WriteLine("Addition of two integers::::::::::::::::" + mthover.Addition(2, 5));
        // ドット演算子でmthoverのAdditionに(2, 5)渡し.以降同じく渡し
        Console.WriteLine("Addition of two double type values::::::" + mthover.Addition(0.40f, 0.50f));// 型指定サフィックスf(引数float用)
        Console.WriteLine("Addition of three integers::::::::::::::" + mthover.Addition(2, 5, 5));
        Console.WriteLine("Addition of three double type values::::" + mthover.Addition(0.40f, 0.50f, 0.60f));
        //Console.WriteLine($"Addition of three double type values::::{mthover.Addition(0.40f, 0.50f, 0.60f)}");
    }
}
コード例(params int[] arguments)int add = 0; foreach in
    class Program

    {

        static void Main(string[] args)// Main 実行文

        {

            ADDParameters(1);//1回実行

            ADDParameters(1, 2, 3, 4, 5);//計15回実行

            ADDParameters1("sha", "rad");

            ADDParameters1("How", " are", " you", " ?");

            Console.ReadKey();

        }

        //for interger calculation

        public static void ADDParameters(params int[] arguments)// 引数可変個params

        {

            int add = 0;// 定義

            foreach (int argu in arguments)//foreach AinB BそれぞれをAに格納実行

            {

                add += argu;// addとarguが1ずつ増加(argu初期0)

            }

            Console.WriteLine(add);// 12345…

        }

        public static void ADDParameters1(params string[] arguemnts)// 引数可変個params

        {

            string add = "";// 定義

            foreach (string argu in arguemnts)//foreach AinB

            {

                add += argu;// addとarguが1ずつ増加

            }

            Console.WriteLine(add);//12345…

        }

    }

引数違えば可,手続き自動判別(渡す用にnewとdot),サフィックス使える
可変個数の場合params

##インデクサ

uncp {配列&インデクサgetsetvalue} s (メソッドはMainあればいいから第一クラスにない) インデクサ(public T this[int i]{getsetvalue}の部分) Main{新配列new;入力値がsetアクセサ使用しその後の処理文がgetアクセサ使用

多次元インデクサは、ライブラリのユーザビリティを大幅に低下させるらしく必要になれば学習する

コード例(基本)
アクセスレベル 戻り値の型 this[添字の型 添字]  public int this[int i]キーワード "this"をインデクサ演算子([])で受け取
{
  set
  {
    // setアクセサ
    //  ここに値の変更時の処理を書く。
    //  value という名前の変数に代入された値が格納される。
    //  添字が使える以外はプロパティと同じ。
  }
  get
  {
    // getアクセサ
    //  ここに値の取得時の処理を書く。
    //  メソッドの場合と同様に、値はreturnキーワードを用いて返す。
    //  こっちも添字が使える以外はプロパティと同じ。
  }
}

未確認飛行c:インデクサ

コード例(添字の下限上限)
いずれやる。複雑すぎて脳内メモリぱんく [未確認飛行c:インデクサ](https://ufcpp.net/study/csharp/oo_indexer.html)
namespace Indexer
{
    class BoundArray
    {
        int[] array;// 配列
        int lower;   // 配列添字の下限

        public BoundArray(int lower, int upper)// 引数 //③
        {
            this.lower = lower;// thisメソッドの配列添字の下限//④
            array = new int[upper - lower + 1];// arrayにindexer 引数upper-lower+1 9-1=8⑦
        }

        public int this[int i]// indexer 添字i //⑤
        {
            set { this.array[i - lower] = value; }// set 値変更時の処理 valueに代入される
            get { return this.array[i - lower]; }// get 値取得時の処理 returnで返す  ⑥
        }
    }

    class Program
    {
        static void Main()
        {
            BoundArray a = new BoundArray(1, 9);//①

            for (int i = 1; i <= 9; ++i)// for(初期化子:条件の間ループ:ループ時代入) iの初期値は1//②
                a[i] = i;// aがi番目=i これがループされる iは最初1なので aが1番目

            for (int i = 1; i <= 9; ++i)
                //Console.Write("a[{0}] = {1}\n", i, a[i]);// 上記for反映されながら、これがループされる
                Console.WriteLine($"a[{i}] = {a[i]}\n");
            // aがBoundArrayのnew(1,9参照渡し,lower1 upper9).その[i]番目(iはindexer引数i)
            // Main実行するとforループ,まずaはBoundArray(1,9)の時の値と定義したので参照私で上記メソッド(BoundArray)に渡される
            //上記メソッドはint lowerが1,upperが9を受けarrayの式によりthisのlower(下のインデクサ引数int iに1渡されget式i-lowerにより...
            //0)

        }
    }
}
コード例(MSDN例)

普通の配列(C S var 初期宣言 = new[] {"あああ","daniel"};で定義していたが、
インデクサはプロパティに[]使えるやつなのでC P S:accessLV 戻り値型 this[int i]{get...set...=value}
配列と同じようにオブジェクトにインデックスを作成
get アクセサーは値を返します。 set アクセサーは値を割り当てます。
this キーワードは、インデクサーの定義に使用されます。
value キーワードは、set インデクサーによって割り当てられる値の定義に使用されます。

namespace Indexer01
{
    class SampleCollection<T>// <T> 型は使う時に指定 後述SampleCollection<string>
    {
        private T[] arr = new T[100];// Tは型ではないが後に使う時指定する後回しできる。newで配列を宣言(new T[100])

        public T this[int i]// []を使用できるようインデクサ定義 this必須(accessLV 戻り値型 this[int i])
        {
            get => arr[i];// 値を返す(arr[i]番目返す)
            set => arr[i] = value;// 値を割り当てる value必須(arr[i]番目割当)
        }
    }

    class Program
    {
        static void Main()
        {
            var stringCollection = new SampleCollection<string>();// newでSampleCollectionのstringだけ抽出定義
            stringCollection[0] = "Hello, World.";// 上で定義したものの0番目の定義
            Console.WriteLine(stringCollection[0]);//0番目
        }
    }
    // output: Hello, World.
}
コード例(MSDN例2度目)
// uncp {配列&インデクサgetsetvalue} ~~s~~
(メソッドはMainだけあればいいからこのクラスにいらない) インデクサ(public T this[int i]{getsetvalue}の部分)
namespace Indexer01
{
    class SampleCollection<T>
    {
        private T[] arr = new T[100];

        public T this[int i]
        {
            get => arr[i];
            set => arr[i] = value;
        }
    }

    class Program
    {
        static void Main()
        {
            var stringCollection = new SampleCollection<string>();// <>指定.後ろの()はnewに必須
            stringCollection[0] = "Hello, world.";
            Console.WriteLine(stringCollection[0]);
        }
    }
}
コード例(MSDN例:実際の華氏記録コード,どこでgetset使うかvalue)
[MSDN:華氏](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/indexers/using-indexers)
using System;

namespace FahrenheitBase
{
  class TempRecord
  {
    // Array of temperature values
    private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F, 
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };

    // To enable client code to validate input 
    // when accessing your indexer.
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration.
    // If index is out of range, the temps array will throw the exception.
    public float this[int index]
    {
        get
        {
            return temps[index];
        }

        set
        {
            temps[index] = value;
        }
    }
  }

  class MainClass
  {
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
        // Use the indexer's set accessor
        tempRecord[3] = 58.3F;
        tempRecord[5] = 60.1F;

        // Use the indexer's get accessor
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();

    }
  }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */
コード例(MSDN例:華氏記録をC#7対応)
[MSDN:華氏](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/indexers/using-indexers) getsetがラムダで省略(return無value有)、新たな配列用newでvar ~ 可能、throw式化は使わないか
using System;

namespace FahrenheitBase
{
    class TempRecord
    {
        private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };// 配列

        public int Length// インデクサにアクセスする際にクライアントが入力を検証できるようにする。
        {
            get => temps.Length;// temps.Length範囲外の番地スロー// get { return temps.Length; }
        }

        public float this[int index]// インデクサ宣言.[index]範囲外のtempsスロー
        {
            get => temps[index];         // get{return temps[index];}
            set => temps[index] = value;// set{temps[index] = value;}
        }
    }

    class MainClass
    {
        static void Main()
        {
            var tempRecord = new TempRecord();//new演算子で渡す用にtempRecord定義 ()はnew使う時必須の記述

            tempRecord[3] = 58.3F;// 入力値がsetアクセサ使用
            tempRecord[5] = 60.1F;

            for (int i = 0; i < 10; i++)//getアクセサに使用
            {
                Console.WriteLine($"Element #{i} = {tempRecord[i]}");
            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();

        }
    }

クラスで配列を索引付け(index)できる(出席番号(何年何組何番)、
顧客情報(pinアドレス 電話番号等)その他索引付けして何でも検索可能。
C#7からgetsetがラムダで省略(return無value有)、新たな配列用newでvar ~ 可能

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?