オブジェクト指向: プロパティ・メソッド

プロパティ

アクセサ

メンバ変数(インスタンス変数)に外部からアクセスすつ為のメソッド
get(set)アクセサ、getter(setter)と呼ばれる。

private class clsParent{
 private int _member1;
 public int member1
 {
  //getアクセサ
  get { return this._member1; }
  //setアクセサ
  valueは宣言不要
  set { _member1 = value; }
 }

 //↑の省略形
 public int member2 { get; set; }

 public void SubRoutin1(){
  ~
 }
}

public void main()
{
 clsParent myParent = new clsParent();
 myParent.member1 = 5;
 int x = myParent.member1;

 myParent.member2 = 10;
 int y = myParent.member2;

 myParent.SubRoutin1();
}

class MyClass
{
private:
  int _member;
public:
  int GetMember();
  void SetMember(int member);
};

int MyClass::GetMember()
{
  return this->_member;
}

void MyClass::SetMember(int member)
{
  this->_member = member;
}

int main(array<System::String ^> ^args)
{
  MyClass *myClass = new MyClass();
  myClass->SetMember(10);
  Console::WriteLine(myClass->GetMember());
  // →10
  delete myClass;
  
return 0;
}

※Getter/Setterを同名でオーバーロードすると呼び出し側からは使いやすくなる。
オーバーロード」参照

Private Class clsTest()
 Dim _Menber As Integer
 
 Public Property Menber As Integer
  ‘getアクセサ
  Get
   Return _Member
  End Get
  ‘setアクセサ
  Set(ByVal prm As String)
   _Member = prm
  End Set
 End Property
 
 Public Sub SubRoutine()
  ~
 End Sub
End Class

Dim myTest As New clsTest()
myTest.Member = 10
Dim x As Integer = myTest.Member
Call myTest.SubRoutine()

クラスモジュール
MyClass.cls

Dim pMyVal As Integer
Dim pMyObj As Object

‘参照(読み取り)専用:データ型
Public Property Get Getter() As Integer
  Getter = pMyVal
End Property

‘参照(読み取り)専用:オブジェクト型
Public Property Get Getter() As Object
  Set Getter = pMyVal
End Property

‘設定(書き込み)専用:データ型
Public Property Let Letter(ByVal prm As Integer)
  pMyVal = prm
  ※正確には↓ の様に書くが「Let」は省略可能。
  Let pMyVal = prm
End Property

‘設定(書き込み)専用:オブジェクト型
Public Property Set Setter(ByVal prm As Object)
  Set pMyObj = prm
End Property

‘イニシャライザー
Private Sub Class_Initialize()
  Call MsgBox("Initialize()")
End Sub

‘ファイナライザー
Private Sub Class_Terminate()
  Call MsgBox("Terminate()")
End Sub

※呼び出し元
Private Sub Form_Load()

  Dim myClass As myClass
  Set myClass = New myClass
  
  myClass.Letter = 5
  Let myClass.Letter = 5
  
  Dim myObj As Object
  Set myClass.Setter = myObj
  
  Debug.Print (myClass.Getter)
  
  Set myClass = Nothing

End Sub

private class clsParent{
 private int _member1;
 public int member1
 {
  //getter
  get { return this._member1; }
  //setter
  valueは宣言不要
  set { _member1 = value; }
 }

 public void SubRoutin1(){
  ~
 }
}

public void main()
{
 clsParent myParent = new clsParent();
 myParent.member1 = 5;
 int x = myParent.member1;

 myParent.member2 = 10;
 int y = myParent.member2;

 myParent.SubRoutin1();
}

function Calc1(prm1, prm2){
 this.member1 = prm1;
 this.member2 = prm2;
}
var number = new Calc1(1, 2);
var x = number.member1 + number.member2;
⇒ x : 3

number.member1 = 10;
number.member2 = 20;
var y = number.member1 + number.member2;
⇒ x : 30

var Calc2 = function(prm1, prm2){
 this.member1 = prm1;
 this.member2 = prm2;
};

var number = new Calc2(100, 200);
var x = number.member1 + number.member2;
⇒ x : 300

number.member1 = 1000;
number.number2 = 2000;
var y = number.member1 + number.member2;
⇒ y : 3000

$p = new MyClass();

$p->SetMyVal("aaa");
$val = $p->GetMyVal
$val:aaa

class MyClass{
 private $_myVal;
 
 function SetMyVal($prm)
 {
  $this->_myVal = $prm;
 }
 function GetMyVal()
 {
  return $this->_myVal;
 }
}

読み取り・書き込み専用属性

//フィールドをprivate属性にし、アクセサを読み取り用しか定義しない。
int _member;
public int member{ get; }

int _member;
public int member{ set;}

宣言時、コンストラクタでのみ設定可能なインスタンス変数
(固定値(const)と違い、インスタンス毎に値を変えられる)
public class Test
{
  private readonly int member = 5;
  public Test(int init)
  {
    this.member = init;
  }
  
  public int Member
  {
    get { return this.member; }
    ⇒ OK
    set { this.member = value; }
    ⇒ ビルドエラー
  }
}

 Dim _Menber As Integer
 Public ReadOnly Property Menber As Integer
  Get
   Return _Member
  End Get
 End Property
 
 Dim _Menber As Integer
 Public WriteOnly Property Menber As Integer
  Set(ByVal prm As String)
   _Member = prm
  End Set
 End Property

インデクサ

※インスタンス自身をプロパティとして扱う

using System.Collections.Generic;
public class MyList<T>
{
  T[] _list = new T[1000];
  public T this[int index]
  {
    get { return _list[index]; }
    set { _list[index] = value; }
  }
}

public void Form1_Load(object sender, EventArgs e)
{
  MyList<int> myList = new MyList<int>();
  
  for (var i = 0; i < 10; i++)
  {
    myList[i] = i;
    ※インスタンスをプロパティとして扱っている
  }
}

Imports System.Collections.Generic
Public Class MyList(Of T)
  Dim _list(1000) As T
  
  //デフォルトのプロパティを宣言
  Default Public Property list(ByVal index As Integer) As T
    Get
      Return _list(index)
    End Get
    Set(value As T)
      _list(index) = value
    End Set
  End Property
  
End Class

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
  
  Dim myList As New MyList(Of Integer)
  
  For i As Integer = 0 To 10 Step 1
    myList(i) = i
    ※インスタンスをプロパティとして扱っている
  Next i
  
End Sub

メソッド

クラス内サブルーチン
public宣言する事でクラス外部から呼ばせる事が可能。

public class Parent
{
 private int member;
 public void SubRoutine(int prm1, int prm2)
 { this.member = prm1 + prm2; }
}

public void main()
{
  Parent p = new Parent();
  p.SubRoutine(prm1:5, prm2:10);
}

// インライン定義
class MyClass
{
private:
  int ~;
public:
  void sayMember(){
    ~
  }
};

// 外部宣言
class MyClass
{
private:
  int ~;
public:
  // プロトタイプ宣言
  void sayMember();
};

void MyClass::sayMember()
{
  ~;
}

int main(array<System::String ^> ^args)
{
  インスタンスをスタック領域に作成
  MyClass myClass
  myClass.sayMember();
  
  インスタンスをヒープ領域に作成
  MyClass *myClass = new MyClass();
  myClass->sayMember();
  delete myClass
  
  return 0;
}

※スタック領域/ヒープ領域については、「メモリ管理/ヒープ領域」参照

Public Class Parent
 Private member As Integer
 Public Sub SubRoutine(
   ByVal prm1 As Integer, _
   ByVal prm2 As Integer)
   me.member = prm1 + prm2
 End Sub
End Class

Public Sub Main()
  Dim p As New Parent()
  p.SubRoutine(prm1:=5, prm2:=10)
End Sub

C#と同じ
var Parent = function(){
  this.answer;
  this.add = function(prm1, prm2){
    this.answer = prm1 + prm2;
  };
};
$p = new MyClass();

$p->Method();
aaa

class MyClass{
 function Method(){
  print("aaa");
 }
}

オーバーロード

多重定義
引数・戻り値の異なる同名のメソッドを定義する事。
似た機能を同名にする事で直感的に使いやすくなる。
設計時には名前衝突も起こり難い。

private class Parent
{
 public int member{ get; set; }
 
 public void SubRoutine(int prm1, int prm2)
 { this.member = prm1 + prm2; }
 
 //オーバーロード(引数が違う)
 public void SubRoutine()
 { Console.WriteLine(this.member); }
 
 //オーバーロード(戻り値が違う)
 ※引数も変える必要あり
 public int SubRoutine(int prm1, int prm2, int prm3)
 {
  return prm1 + prm2 + prm3;
 }
}
※C++はクラス内に限らず通常メソッドでもオーバーロード可能

※Getter/Setterを同名でオーバーロードした例
class MyClass
{
private:
  int _member;
public:
  int Member();
  void Member(int member);
};

int MyClass::Member()
{
  return this->_member;
}

void MyClass::Member(int member)
{
  this->_member = member;
}

int main(array<System::String ^> ^args)
{
  MyClass *myClass = new MyClass();
  myClass->Member(10);
  Console::WriteLine(myClass->Member());
  // →10
  delete myClass;

  return 0;
}

Public Class Parent
 Private member As Integer
 Public Sub SubRoutine(
   ByVal prm1 As Integer, _
   ByVal prm2 As Integer)
   me.member = prm1 + prm2
 End Sub
 
 ’オーバーロード(引数が違う)
 Public Overloads Sub SubRoutine()
  Console.WriteLine(me.member)
 End Sub
 
 ’オーバーロード(引数が違う)
 ※引数も変える必要あり
 Public Overloads Function SubRoutine(
   ByVal prm1 As Integer, _
   ByVal prm2 As Integer, _
   ByVal prm3 As Integer) As Integer
  Return prm1 + prm2 + prm3
 End Sub
End Class
private class Parent
{
 private int member;
 
 public void SubRoutine(int prm1, int prm2)
 { this.member = prm1 + prm2; }
 
 //オーバーロード(引数が違う)
 public void SubRoutine()
 { Console.WriteLine(this.member); }
 
 //オーバーロード(戻り値が違う)
 ※引数も変える必要あり
 public int SubRoutine(int prm1, int prm2, int prm3)
 {
  return prm1 + prm2 + prm3;
 }
}
var Parent = function(){
  this.answer;
  this.add = function(prm1, prm2){
    this.answer = prm1 + prm2;
  };
};

var Child = function(){};

//継承
Child.prototype = new Parent();
Child.prototype.minus = function(prm){
  this.answer -= prm;
};

var child = new Child();
child.add(1,1);
child.minus(1);
⇒this.answer = 1

演算子のオーバーロード

class MyClass
{
 int x, y;
 
 public MyClass() { x = y = 0; }
 public MyClass(int i, int j) { x = i; y = j; }
 
 public static MyClass operator +(MyClass prm1, MyClass prm2)
 {
  MyClass result = new MyClass();
  
  result.x = prm1.x + prm2.x;
  result.y = prm1.y + prm2.y;
  
  return result;
 }

}

class Program
{
 static void Main(string[] args)
 {

  MyClass a = new MyClass(1, 1);
  MyClass b = new MyClass(10, 10);
  MyClass c = new MyClass();
  c = a + b;
  c.x:11 c.y:11
  +はMyClassとMyClassの値が足されている
 }
}

class MyClass
{
 int x, y;
 
 public MyClass() { x = y = 0; }
 public MyClass(int i, int j) { x = i; y = j; }
 
 public static MyClass operator +(MyClass prmObj, int prmInt)
 {
  MyClass result = new MyClass();
  
  result.x = prmObj.x + prmInt;
  result.y = prmObj.y + prmInt;
  
  return result;
 }
}

class Program
{
 static void Main(string[] args)
 {

  MyClass a = new MyClass(1, 1);
  MyClass result = new MyClass();
  
  result = a + 5;
  result.x:6 result.y:6
  +はMyClassとintの値が足されている
 }
}

class MyClass
{
private:
 int myInt1;
 int myInt2;
public:
 MyClass(int prm1, int prm2);
 void Show();
 MyClass operator+(MyClass obj);
};

MyClass::MyClass(int prm1 = 0, int prm2 = 0)
{
 this->myInt1 = prm1;
 this->myInt2 = prm2;
};

void MyClass::Show()
{
 cout << "myInt1:" << this->myInt1 << endl;
 cout << "myInt2:" << this->myInt2 << endl;
}

MyClass MyClass::operator+(MyClass obj)
{
 MyClass m;
 m.myInt1 = this->myInt1 + obj.myInt1;
 b.myInt1 + c.myInt2
 m.myInt2 = this->myInt2 + obj.myInt2;
 b.myInt2 + c.myInt2

 return m;
 結果をa.myInt1、a.myInt2に格納
 インスタンス:mはここで破棄される
}

int _tmain(int argc, _TCHAR* argv[])
{
 MyClass a(0, 0);
 MyClass b(10, 20);
 MyClass c(50, 60);

 a = b + c;
 オーバーロードした+を利用
 bの+関数へcを渡し、結果をaに格納する
 a = b.+(c)

 a.Show();
 →myInt1:60 myInt2:80

 return 0;
}

クラス外にオーバーロード演算子を定義
パターン1
class MyClass
{
private:
 int myInt;
public:
 MyClass(int prm){ this->myInt = prm; };
 int Getter(){ return this->myInt; }
};

通常の+演算子をオーバーロード
MyClass operator+(int prmI, MyClass prmM)
{
 return MyClass(prmI + prmM.Getter());
}

int _tmain(int argc, _TCHAR* argv[])
{

 MyClass a(0);
 MyClass b(100);
 
 a = 1000 + b;
 オーバーロード+に引数1000とbインスタンスを渡し、結果をaに格納
 (なぜか)左側数値が第一引数、右側オブジェクトが第二引数
 →a.myInt:1100

 return 0;
}

パターン2
MyClass operator+(MyClass m1, MyClass m2)
{
 return MyClass(m1.Getter() + m2.Getter());
}

int _tmain(int argc, _TCHAR* argv[])
{

 MyClass a(0);
 MyClass b(100);
 MyClass c(100);
 
 a = b + c;
 オーバーロード+に引数:aインスタンスとbインスタンスを渡し、結果をaに格納
 (なぜか)左側数値が第一引数、右側オブジェクトが第二引数
 →a.myInt:200

 
 return 0;
}

メンバーの追加(JS)

//定義済みクラスに対してメンバー追加
var MyClass = function(){};

MyClass = function(){
 this.member1;
};
MyClass = function(){
 this.add1 = function(){
  this.member1 += 1;
 };
};

var myclass = new MyClass();
myclass.member1 = 5;
myclass.add1();
// ⇒ myclass.member1 = 6;

//インスタンスに対してメンバー追加
member.Minus = function(){
 return this.member1 – this.member2;
};
number.Minus();
⇒-1000;

var newMember = new Calc2();
newMember.Minus();
//↑ 未定義エラー

インスタンス作成時のメソッドのコピー

※プロパティはインスタンス毎に異なる値を持たせる意味はあるが、
 全く同じ挙動をするメソッドを全インスタンスにコピーするのはメモリの無駄。
 その為、例えばWebシステム用言語JavaScriptではprototype等を用いてメソッドのインスタンスへのコピーを省略する仕様を持つ。
 しかしC#ではメソッドはインスタンスにコピーされず、クラスを参照する為のポインタがコピーされるのみ。
 クラスのコピー(インスタンス作成)の際の、メソッドに関するメモリ制御を考慮する必要はない。
※C#と同様。
//プロトタイプ(インスタンス時にメモリにコピーされないメンバー定義)
※プロパティはインスタンス毎に異なる値を持たせる意味はあるが、
 全く同じ挙動をするメソッドを全インスタンスにコピーするのはメモリの無駄。
 ブラウザの読み込み毎にインスタンスを作成するWEBシステムの特性を考慮し、
 メソッドについては通常、以下の機能を用いてメモリーコピーを行わせない。

MyClass.prototype.plus10 = function(number){
 this.member1 += 10;
 this.member2 += 10;
}
var myclass = new MyClass()
myclass.plus10()

//プロトタイプ定義の別の書き方
MyClass.prototype = {
 this.plus10 = function(number){
  this.member1 += 10;
  this.member2 += 10;
 }
}