카테고리 없음

[C# / .NET] Java와 다른 문법

영국너구리 2022. 4. 7. 22:04

Java와 다른 것 위주로 기술

로컬함수

로컬함수는 메소드 안에서 선언되고, 선언된 메소드 안에서만 사용되는 특별한 함수

클래스의 멤버가 아니기 때문에 메소드가 아니라 함수라고 부름

class SomeClass
{
	public void SomeMethod()
    {
    	int count = 0;
        SomeLocalFunction(1,2); //로컬 함수 호출
        
        void SomeLocalFunction(int a, int b) //로컬 함수 선언
        {
        	//Do Some Work
            Console.WriteLine($"count : {++count}"); //로컬 함수는 자신이 속한 메소드의 지역 변수를 사용할 수 있음
        }
    }
}

 

선택적 인수

메소드의 기본변수는 기본값을 가질 수 있음 

void MyMethod(int a, int b = 0)
{
	Console.WriteLine("{0}, {1}", a, b);
}

이와 같이 기본 값을 가진 매개변수는 메소드 호출시 해당 인수를 생략할 수 있음

MyMethod(3); 

MyMethod(3, 4);

 

 

가변 개수의 인수

프로그래밍을 하다 보면 그저 인수의 '개수' 개수가 다르다는 이유만으로 똑같은 메소드를 여러 가지 버전으로 오버로딩하고 싶을 때가 많음 

이런 경우를 위해 C#은 '가변 개수의 인수'라는 기능을 제공함 

가변 개수의 인수란, 그 개수가 유연하게 변할 수 있는 인수를 뜻함 

이것을 이용하면 다음과 같이 입력되는 모든 인수의 합을 구하는 Sum() 메소드를 오버로딩하지 않고도 구현할 수 있음

int total = 0;

total = Sum(1);
total = Sum(1,2);
total = Sum(1,2,3,4);
total = Sum(1,2,3,4,5,6);
total = Sum(1,2,3,4,5,6,7,8);

 

가변 개수의 인수는 params 키워드와 배열을 이용해서 선언함 

다음은 가변 개수의 인수를 이용해서 모든 합을 구해 반환하는 Sum() 메소드의 구현임 

이렇게 구현한 메소드는 앞에서 본 코드에서처럼 인수의 개수를 달리해서 호출할 수 있음

int Sum( params int[] args ) 
{
	int sum = 0;
    
    for(int i=0; i<args.Length; i++){
		sum += args[i];    
    }
    
    return sum;
}

 

 

메소드의 결과를 참조로 반환하기

참조 반환값을 이용하면 메소드의 호출자로 하여금 반환받은 결과를 참조로 다룰 수 있도록 함

class SomeClass{
	int SomeValue = 10;
    
    public ref int SomeMethod(){
    	return ref SomeValue; 
    }
}

Main ..(){
	//ref 사용 안 하는 예제 
    SomeClass obj = new SomeClass();
    int result = obj.SomeMethod();
    
    //ref 사용하는 예제
    SomeClass obj = new SomeClass();
    ref int result = ref obj.SomeMethod();
}
using System; 

namespace RfReturn {
	class Product{    	
        private int price = 100;
        
        public ref int GetPrice(){
        	return ref price;
        }
        
        public void PrintPrice(){
        	Console.WirteLine($"Price : {price}");            
        }
    }
    
    class MainApp{
    	static void Main(String[] args){
        	Product carrot = new Product();
            ref int ref_local_price = ref carrot.GetPrice();
            int normal_local_price = carrot.GetPrice(); 
            
            carrot.PrintPrice();
            Console.WriteLine($"Ref Local Price : {ref_local_price}"); 
            Console.WirteLine($"Normal Local Price : {normal_local_price}");
            
            ref_local_price = 200;
            
            carrot.PrintPrice(); 
            Console.WriteLine($"Ref Local Price : {ref_local_price}"); 
            Console.WirteLine($"Normal Local Price : {normal_local_price});
            
        }
    }
}


/*
========== 결과 ==========

Price : 100
Ref Local Price : 100
Normal Local Price : 100

Price : 200
Ref Local Price : 200
Normal Local Price : 100
*/

 

 

goto

goto? 점프문 

 

goto 레이블;

레이블 : 

//이어지는 코드

 

{
	Console.WriteLine(" 1 "); 
    
	goto JUMP;
    
	Console.WriteLine(" 2 ");
	Console.WriteLine(" 3 ");
    
 	JUMP:
	Console.WriteLine(" 4 ");
}

/*
===== 결과 =====
1
4
*/

 

foreach

int[] arr = new int[]{0,1,2,3,4};

foreach(int a in arr) //java는 ? for(int a : arr){}
{
	Console.WriteLIne(a);
}

 

 

switch식 

Swtich문이 아닌 switch식

//switch문 
int score = Console.ReadLine();
string grade;

switch(score)
{
	case 90:
    	grade = "A";
        break;
    case 80:
    	grade = "B";
        break;
    case 70:
    	grade = "C";
        break;
    case 60:
    	grade = "D";
    	break;
    default:
    	grade = "F";        
}

//switch식
int score = Console.ReadLine();
string grade = score switch{
	90 => "A",
	80 => "B",
	70 => "C",
	60 => "D",
	_ => "F"
};

//switch식 - when 절을 활용
bool repeated = true;
string grade = score switch{
	90 when repeted == true => "B+",
	90 => "A",
	80 => "B",
	70 => "C",
	60 => "D",
	_ => "F"
}

 

상속

class Base
{
	int 
	public void BaseMethod()
    {
    	Console.WriteLine("BaseMethod");
    }
}

class Derived : Base
{
	//Derived 클래스는 Base 클래스를 상속했으므로 BaseMethod()를 가짐
    
    public void DerivedMethod()
    {
    	base.BaseMethod(); //java super 같은
    }
}
class Base
{
	protected String Name;
    public Base(string Name)
    {
    	this.Name = Name;
    }
}

class Derived : Base
{  
    public DerivedMethod(string Name) : base(Name) //Base(string Name) 호출
    {
    	base.BaseMethod(); //java super 같은
    }
}
using System;

namespace Inheritance
{
	class Base
    {
    	protected String Name; 
        public Base(string Name)
        {
        	this.Name = Name;
            Console.WriteLine($"{this.Name}.Base()");
        }
        
        ~Base()
        {
        	Console.WriteLine($"{this.Name}.~Base()");
        }
        
        public void BaseMethod()
        {
        	Console.WriteLine($"{Name}.BaseMethod()");
        }
    }
    
    class Derived : Base
    {
    	public Derived(string Name) : base(Name)
        {
        	Console.WriteLine($"{this.Name}.Derived()");
        }
        
        ~Derived()
        {
        	Console.WriteLine($"{this.Name}.~Derived()");
        }
        
        public void DerivedMethod()
        {
        	Console.WriteLine($"{Name}.DerivedMethod()");
        }
    }
    
    class MainApp
    {
    	static void Main(string[] args)
        {
        	Base a = new Base("a");
            a.BaseMethod();
            
            Console.WriteLine();
            
            Derived b = new Derived("b");
            b.BaseMethod();
            b.DerivedMethod();
        }
    }
}


/*
======== 결과 ========
a.Base()
a.BaseMethod()

b.Base()
b.Derived()
b.BaseMethod()
b.DerivedMethod()

*/

상속 봉인도 가능 

sealed class Base{ ... }

class Derived : Base { ... } => 컴파일 에러남

 

형식 변환을 위한 연산자 is, as

  • is : 객체가 해당 형식에 해당하는지 검사하여 그 결과를 bool 값으로 반환함 (java의 instanceof 와 같아 보임)
Base bs = new Derived();
Derived dv;

if(bs is Derived)
{
	dv = (Derived)bs;
}

 

  • as : 형식 변환 연산자와 같은 역할을 함. 다만 형식 변환 연산자가 변환에 실패하는 경우 예외를 던지는 반면에 as 연산자는 객체 참조를 null로 만든다는 것이 다름.
Base bs2 = new Derived();

Derived dv2 = bs2 as Derived; 
if(dv2 != null) //bs2가 Derived 형식 변환에 실패했다면 null이 됨, 하지만 이코드에서는 안전하게 형식 변환이 이루어짐
{
	Console.WriteLine("complete"); 
}

 

오버라이딩(Overriding)

자식 클래스에서 부모 클래스의 메소드를 오버라이딩 하기 위해서는 부모 메소드에 virtual을 명시해주어야 하며,

자식 메소드에는 override를 명시해주어야 함 

class Base{
	public virtual void UtilMethod(){
    	Console.WriteLine("Base Util Method()");
    }
}

class Derived{
	public override void UtilMethod(){
    	Console.WriteLine("Derived Util Method()");
    }
}

const vs readonly

  • 상수 : 변하지 않는 값 
  • 변수 : 자유롭게 변하는 값
  • 읽기 전용 : 상수와 변수 사이 그 중간 어딘가
class D {
	private readonly int r;
    private const int i ; //컴파일 에러 발생 => 반드시 선언과 동시에 초기화를 해주어야 함 
 	
    public D(int n){
    	r = n; //생성자 안에서만 초기화가 가능
    }
    
    public Method(int n){
    	r = n; //컴파일 에러
    }
}

 

중첩 클래스

class A{
    class B{
    }
}

중첩클래스를 쓰는 이유? 

  • 클래스 외부에 공개하고 싶지 않은 형식을 만들고자 할 때
  • 현재 클래스의 일부분처럼 표현할 수 있는 클래스를 만들고자 할 때

 

프로퍼티

java에서는 class의 private 변수에 접근하기 위해 보통 아래와 같이 사용했었음 

class MyClass{
	
    private int myField;
    public int GetMyField() { return myField; }
    public void SetMyField(int num) { myField = num; }
    
}

그런데 C#의 프로퍼티를 활용하면 아래와 같이 간단하게 표현 할 수 있고, '='을 통해 바로 변수 값을 설정할 수 있음

class 클래스 이름 
{
    데이터형식 필드이름;
    접근한정자 데이터형식 프로퍼티이름
    {
    	get
        {
        	return 필드이름;
        }
        
        set
        {
        	필드이름 = value;
        }
    }
}

프로퍼티 선언 문법에서 get {...} set {...} 을 일컬어 접근자라고 함 

value 키워드는 선언한 적은 없지만, c# 컴파일러는 set 접근자의 암묵적 매개변수로 간주함

class MyClass
{
	private int myField;
	public int MyField
	{
	get
	{
		return myField;
	}

	set
	{
		myField = value;
	}
	}
}

...

MyClass obj = new MyClass();
obj.MyField = 3;

 

C# 3.0부터는 자동 구현 프로퍼티가 가능함 

public class NameCard
{
    public String Name
    {
    	get;set;
    }
}

..

//호출시에
NameCard nc = new NameCard
{
	Name = "영국너구리"
}

 

더불어 C# 7.0부터는 자동 구현 프로퍼티를 선언함과 동시에 초기화를 수행할 수 있음 

public class NameCard
{
    public string Name{ get; set; } = "Unknown";
    public string PhoneNumber{ get; set; } = "000-0000";
}