반응형
 
#define SOL_ALL_SAFETIES_ON 1

#include <sol/sol.hpp>
#include <assert.hpp>

int main() {
	sol::state lua;
	int x = 0;
	lua.set_function("beep", [&x]{ ++x; });
	lua.script("beep()");
	c_assert(x == 1);

	sol::function beep = lua["beep"];
	beep();
	c_assert(x == 2);

	return 0;
}

 

#define SOL_ALL_SAFETIES_ON 1

#include <sol/sol.hpp>
#include <assert.hpp>

struct vars {
	int boop = 0;

	int bop () const {
		return boop + 1;
	}
};

int main() {
	sol::state lua;
	lua.new_usertype<vars>("vars", 
		"boop", &vars::boop,
		"bop", &vars::bop);
	lua.script("beep = vars.new()\n"
		"beep.boop = 1\n"
		"bopvalue = beep:bop()");

	vars& beep = lua["beep"];
	int bopvalue = lua["bopvalue"];

	c_assert(beep.boop == 1);
	c_assert(lua.get<vars>("beep").boop == 1);
	c_assert(beep.bop() == 2);
	c_assert(bopvalue == 2);

	return 0;
}

 

 

ref : https://sol2.readthedocs.io/en/latest/

반응형
반응형

std::move 와 std::forward 는 lvalue, rvalue 의 개념과 엮여서 조금 헷갈린다. 어떻게 동작하는지, 심지어 왜 필요한지 등. 아래의 코드로 간단히 정리해본다.

표면적으로는 std::move 는 주어진 입력인자를 rvalue 로 변환해주는 역할을 한다. 아래의 코드에서와 같이 a 라는 객체는 lvalue 이고 std::move 가 a 를 받고 b 에 리턴하는 것은 rvalue 이다. 이게 다 이다. 이 동작과 move 라는 이름이 무슨 관계란 말인가?

 

이는 rvalue 라는 것과 연관지어서 생각해봐야한다. 클래스는 copy constructor 와 move constructor 를 가질 수 있다. 기본 내장형 또는 custom 형태 든. 보통 copy constructor 는 간단히, 클래스 인스턴스가 가진 모든 것을 복사한다고 생각하면 된다. 따라서 복사 비용이 크다. 반면에 move constructor 는 이렇게 모든 것을 복사할 필요가 없고 따라서 복사비용 (공간 및 시간) 을 효율적으로 해야할 때 사용되는데, 이 move constructor 가 호출되기 위해서는 rvalue 가 필요하다. 그래서 std::move 가 무언가를 ravlue 로 만들어주고 이것이 해당 객체를 'movable' 하도록 만들어주기 때문에 이름이 std::move 인 것이다.

다시한번 생각해봐도 이유가 타당한 것 같다. 어떤 객체가 rvalue 라면, 다시말해 const 의 속성을 가지고 있고 값이 변하지 않는 객체라면, 굳이 비싼 비용을 수반해서 모든 걸 복사할 필요가 없지 않은가?

std::forward 의 경우는 move 와 비슷하나 약간 다르다. std::move 는 입력으로 받은 객체를 무조건 rvalue 로 만들어준다. 그러나 std::forward 는 조건이 맞을 때만 rvalue 로 만들어준다.

※ 참고

- https://www.geeksforgeeks.org/move-constructors-in-c-with-examples/

 

 

Move Constructors in C++ with Examples - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

 

ref : https://blog.naver.com/sheld2/222654277182

반응형
반응형

td::forward가 가장 많이 쓰이는 곳은 보편 참조 매개변수로 받아서 처리하는 로직입니다.

// lvalue를 캐치하는 함수
void Catch(Person& p, const char* name)
{
    cout << name << "lvalue catch" << endl;
}

// rvalue를 캐치하는 함수
void Catch(Person&& p, const char * name)
{
    cout << name << "rvalue catch" << endl;
}

// 전달받은 obj를 std::forward를 통해서 catch 함수로 전달합니다.
template<typename T>
void ForwardingObj(T&& obj, const char* name)
{
    Catch(std::forward<T>(obj), name);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Person p1("ahn", 1985);
    ForwardingObj(p1, "p1\t\t=\t");
    ForwardingObj(std::move(p1), "std::move(p1)\t=\t");

    return 0;
}

수행결과

 

ref : https://jungwoong.tistory.com/53

반응형
반응형

Consumer는 1개의 Type T 인자를 받고 리턴 값이 없는 함수형 인터페이스입니다.

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

}

Example 1 : Consumer

Consumer는 Lambda 표현식으로 구현할 수 있습니다. accept() 호출과 함께 인자를 전달하면 구현된 내용이 수행됩니다.

import java.util.function.Consumer;

public class ConsumerExample1 {

    public static void main(String[] args) {
        Consumer<String> consumer = s -> System.out.println(s.toUpperCase());

        consumer.accept("hello world");
    }
}

Output:

HELLO WORLD

Example 2 : Primitive Consumer

Java는 Primitive type에 대한 Consumer 클래스를 제공합니다. IntConsumer 외에 DoubleConsumer, LongConsumer가 있습니다.

import java.util.function.IntConsumer;

public class ConsumerExample2 {

    public static void main(String[] args) {
        IntConsumer consumer = n -> System.out.println(n * 100);

        consumer.accept(5);

        consumer.accept(10);
    }
}

Output:

500
1000

Example 3 : List.forEach()

List.forEach()는 인자로 Consumer를 받습니다.

다음과 같이 Consumer를 이용하여 List의 모든 아이템들을 출력할 수 있습니다.

 

Output:

APPLE
KIWI
ORANGE

Example 4 : andThen()

andThen()은 Consumer들을 연결해주는 Chaining 메소드입니다.

아래 코드에서 addNumber.andThen(printList).accept(fruits)를 호출하면 addNumber()가 수행된 이후에 printList()가 수행됩니다. 두개의 메소드로 전달되는 인자는 fruits로, 동일한 객체가 전달됩니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerExample4 {

    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "kiwi", "orange");

        Consumer<List<String>> addNumber = list -> {
            for (int i = 0; i < list.size(); i++) {
                list.set(i, (i + 1) + ". " + list.get(i));
            }
        };

        Consumer<List<String>> printList = list -> {
            for (String fruit : list) {
                System.out.println(fruit);
            }
        };

        addNumber.andThen(printList).accept(fruits);
    }
}

Output:

1. apple
2. kiwi
3. orange

실제로 andThen()은 아래처럼 구현되어있습니다.

// Consumer.java
public interface Consumer<T> {
  ...
  default Consumer<T> andThen(Consumer<? super T> after) {
      Objects.requireNonNull(after);
      return (T t) -> { accept(t); after.accept(t); };
  }
}

Example 5 : 함수의 인자로 Consumer 전달 (Higher Order Function)

다음과 같이 Consumer를 인자로 받는 함수를 정의할 수 있습니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerExample5 {

    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "kiwi", "orange");

        Consumer<String> printFruit = fruit -> System.out.println("I like " + fruit);

        forEach(fruits, printFruit);
    }

    static <T> void forEach(List<T> list, Consumer<T> consumer) {
        for (T t : list) {
            consumer.accept(t);
        }
    }

}

Output:

I like apple
I like kiwi
I like orange

 

 

ref : https://codechacha.com/ko/java8-consumer-example/

반응형
반응형

 

그림 1은 런타임 라이브러리 종속 항목을 가진 앱을 보여줍니다. 앱 코드를 검사하는 동안 R8은 MainActivity.class 진입점에서 foo(), faz(), bar() 메서드에 연결할 수 있다고 판단합니다. 그러나 런타임에 OkayApi.class 클래스 또는 baz() 메서드가 앱에서 전혀 사용되지 않으며 R8은 앱을 축소할 때 이 코드를 삭제합니다.

 

\

R8은 프로젝트의 R8 구성 파일 -keep 규칙을 이용하여 진입점을 결정합니다. 즉, keep 규칙은 앱을 축소할 때 R8이 삭제하면 안 되는 클래스를 지정하고 R8은 이 클래스를 앱의 진입점으로 사용할 수 있다고 간주합니다. Android Gradle 플러그인과 AAPT2는 앱의 활동, 보기 및 서비스와 같이 대부분의 앱 프로젝트에서 필요한 keep 규칙을 자동으로 생성합니다. 그러나 추가적인 keep 규칙을 사용하여 이 기본 동작을 맞춤설정해야 하는 경우 유지할 코드를 맞춤설정하는 방법에 관한 섹션을 참조하세요.

대신 앱 리소스의 크기를 줄이는 데만 관심이 있다면 리소스 축소 방법에 관한 섹션을 참조하세요.

 

 

 

유지할 코드 맞춤설정

대부분의 상황에서는 기본 ProGuard 규칙 파일(proguard-android- optimize.txt)만 있으면 R8을 이용하여 미사용 코드를 삭제할 수 있습니다. 그러나 R8에서 정확하게 분석하기 어려운 상황도 있으며 실제로 앱에서 사용하는 코드를 삭제하는 경우도 발생할 수 있습니다. 다음은 코드를 잘못 삭제할 수 있는 몇 가지 예입니다.

  • 앱이 자바 네이티브 인터페이스(JNI)에서 메서드를 호출하는 경우
  • 앱이 런타임에 리플랙션 등을 사용하여 코드를 찾는 경우

앱을 테스트하면 잘못된 코드 삭제로 인한 오류가 나타나지만 삭제된 코드 보고서를 생성하여 삭제된 코드를 검사할 수도 있습니다.

오류를 수정하고 R8이 특정 코드를 유지하도록 하려면 ProGuard 규칙 파일에 -keep 줄을 추가합니다. 예:

 
-keep public class MyClass

또는 유지하려는 코드에 @Keep 주석을 추가할 수 있습니다. @Keep을 클래스에 추가하면 전체 클래스가 그대로 유지됩니다. 이 주석을 메서드나 필드에 추가하면 메서드/필드 및 그 이름뿐만 아니라 클래스 이름도 그대로 유지됩니다. 참고로 이 주석은 축소 사용 방법에 관한 섹션에 설명한 대로 AndroidX 주석 라이브러리를 사용하고 Android Gradle 플러그인과 함께 패키징된 ProGuard 규칙 파일을 포함할 때만 사용할 수 있습니다.

-keep 옵션을 사용하려면 고려해야 하는 사항이 많습니다. 규칙 파일을 맞춤설정하는 방법에 관한 자세한 정보는 ProGuard 설명서를 참조하세요. 문제 해결 섹션에서는 코드를 제거할 때 발생할 수 있는 다른 일반적인 문제를 간략히 설명합니다.

 

 

 

ref : https://developer.android.com/studio/build/shrink-code?hl=ko

반응형

'프로그래밍(Programming) > Java' 카테고리의 다른 글

Java : Consumer  (0) 2022.01.12
스트림 변환 메서드들 표  (0) 2021.12.31
interface default 키워드로 메소드 구현하기  (0) 2021.12.30
[Java] Comparable, comparator  (0) 2021.12.15
[Java] Collection , Map, Tree  (0) 2021.12.15
반응형

 

 

ref : https://www.youtube.com/watch?v=VUh_t_j9qjE&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=171

반응형

'프로그래밍(Programming) > Java' 카테고리의 다른 글

Java : Consumer  (0) 2022.01.12
Java @Keep (Android )  (0) 2022.01.03
interface default 키워드로 메소드 구현하기  (0) 2021.12.30
[Java] Comparable, comparator  (0) 2021.12.15
[Java] Collection , Map, Tree  (0) 2021.12.15
반응형

인터페이스의 default method

인터페이스의 default 메소드

JAVA 8이 등장하면서 interface에 대한 정의가 몇 가지 변경되었다.

default메소드

  • 인터페이스가 default키워드로 선언되면 메소드가 구현될 수 있다. 또한 이를 구현하는 클래스는 default메소드를 오버라이딩 할 수 있다.
    public interface Calculator {
        public int plus(int i, int j);
        public int multiple(int i, int j);
        default int exec(int i, int j){      //default로 선언함으로 메소드를 구현할 수 있다.
            return i + j;
        }
    }

    //Calculator인터페이스를 구현한 MyCalculator클래스
    public class MyCalculator implements Calculator {

        @Override
        public int plus(int i, int j) {
            return i + j;
        }

        @Override
        public int multiple(int i, int j) {
            return i * j;
        }
    }

    public class MyCalculatorExam {
        public static void main(String[] args){
            Calculator cal = new MyCalculator();
            int value = cal.exec(5, 10);
            System.out.println(value);
        }
    }
  • 인터페이스가 변경이 되면, 인터페이스를 구현하는 모든 클래스들이 해당 메소드를 구현해야 하는 문제가 있다. 이런 문제를 해결하기 위하여 인터페이스에 메소드를 구현해 놓을 수 있도록 하였다.

static메소드

    public interface Calculator {
        public int plus(int i, int j);
        public int multiple(int i, int j);
        default int exec(int i, int j){
            return i + j;
        }
        public static int exec2(int i, int j){   //static 메소드 
            return i * j;
        }
    }

    //인터페이스에서 정의한 static메소드는 반드시 인터페이스명.메소드 형식으로 호출해야한다.  

    public class MyCalculatorExam {
        public static void main(String[] args){
            Calculator cal = new MyCalculator();
            int value = cal.exec(5, 10);
            System.out.println(value);

            int value2 = Calculator.exec2(5, 10);  //static메소드 호출 
            System.out.println(value2);
        }
    }
  • 인터페이스에 static 메소드를 선언함으로써, 인터페이스를 이용하여 간단한 기능을 가지는 유틸리티성 인터페이스를 만들 수 있게 되었다.

 

 

ref : https://programmers.co.kr/learn/courses/5/lessons/241

반응형

'프로그래밍(Programming) > Java' 카테고리의 다른 글

Java @Keep (Android )  (0) 2022.01.03
스트림 변환 메서드들 표  (0) 2021.12.31
[Java] Comparable, comparator  (0) 2021.12.15
[Java] Collection , Map, Tree  (0) 2021.12.15
RMI 개념, 분산 시스템이란  (0) 2012.10.31
반응형

 

 

 

TreeSet 에서  비교 클래슬를 생성자에서 받을때는 Comparable 을 상속받은 클래스는 불가하고

Comparator 를 상속받은 클래스를 생성자에 Functor 식으로 넣어서 비교기준으로 삼는 것은 가능하다

class TestCopmp implements Comparator {

    public int a;

    @Override
    public int compare(Object o1, Object o2) {
        return ((Test)o1).a - ((Test)o2).a;
    }
}

 

Set set = new TreeSet( new TestCopmp() );

마치 C++ 에서 Fuctor 를 만든것 처럼 끝 어미도 tor 이다, 사용법도 유사하다 볼수 있다

Comparator 는 sort 시에도 정렬 기준으로 던져줄수 있는 Fuctor 다

 

 

 

위 처럼 생성자에 넣는 것이 아닌 클래스 자체에 비교 함수를 override 하는 방법이 있는데 그것이 Comparable 이다

class Test implements Comparable {
    public int a;

    @Override
    public int compareTo(Object o) {
        return a - ((Test)o).a;
    }
}
//원소 추가함과 동시에 정렬됨
Set set = new TreeSet();

for (int i=0; set.size() < 6;++i)
{
    int nm = (int)(Math.random()* 45) + 1;
    Test ss= new Test();
    ss.a = nm;

    set.add(ss);

}

 

 위 처럼 하면 가능 하지만 Test 클래스가 Comparator 를 상속을 받는것은 가능하나
 객체간의 비교로 사용용도로 사용되진 못한다, 사용하면 크래쉬 (Comparable 로 캐스팅 하지 못한다는 에러 발생)

Exception in thread "main" java.lang.ClassCastException: class Test cannot be cast to 
class java.lang.Comparable (Test is in unnamed module of loader 'app'; 
java.lang.Comparable is in module java.base of loader 'bootstrap')

	at java.base/java.util.TreeMap.compare(TreeMap.java:1563)
	at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:768)
	at java.base/java.util.TreeMap.put(TreeMap.java:777)
	at java.base/java.util.TreeMap.put(TreeMap.java:534)
	at java.base/java.util.TreeSet.add(TreeSet.java:255)
	at comp.main(comp.java:98)

 

 

 

 

 

 

[Comparable 의 사용 예시]

import java.util.*;

//하나의 자바 소스 파일에 작성된 클래스 중 오직 한 클래스만 public으로 선언할 수 있습니다.

//Comparable 상속받아 정렬 기준을 만들수 있다
class Player implements Comparable<Player>
{
    private String name;
    private int score;

    public int getScore(){ return score; }

    @Override
    public int compareTo(Player o) {
        return o.getScore() - this.getScore();
    }

    public Player(String name, int score)
    {
        this.name = name;
        this.score = score;
    }
}

public class comp {

    public static void main(String[] args)
    {
        List<Player> players = new ArrayList<>();
        players.add(new Player("Alice", 899));
        players.add(new Player("Bob", 982));
        players.add(new Player("Chloe", 1090));
        players.add(new Player("Dale", 982));
        players.add(new Player("Eric", 1018));

        //이때 Comparable 이 사용됨
       주석 제거하면 Comparable 가 작동하여 정렬된다
        //Collections.sort(players);


        //comparator 를 통해 클래스 밖에서 정렬 기준을 다르게 하여 정렬 할수 있다
        Comparator<Player> comparator = new Comparator<Player>() {
            @Override
            public int compare(Player a, Player b)
            {
                return a.getScore() - b.getScore();
            }
        };

        //Collection<Integer> sdf = new LinkedList<>();
        Collections.sort(players, comparator);


    }

}

 

 

 

 

 

Comparable 인터페이스

객체의 정렬 기준을 정의하는 첫번째 방법은 정렬 대상 클래스를 자바에서 기본적으로 제공하고 있는 Comparable 인터페이스를 구현하도록 변경하는 것입니다. 이를 적용하면 Player 클래스는 다음과 같이 수정됩니다.

public class Player implements Comparable<Player> {
    // Fields, Getters, Setters 생략

    @Override
    public int compareTo(Player o) {
        return o.getScore() - getScore();
    }
}

Comparable 인터페이스의 compareTo() 메서드를 통해 인자로 넘어온 같은 타입의 다른 객체와 대소 비교가 가능합니다. 메서드를 호출하는 객체가 인자로 넘어온 객체보다 작을 경우에는 음수를 리턴하고, 크기가 동일하다면 0, 클 경우에는 양수를 리턴해야합니다.

게이머 랭키 페이지의 경우 높은 점수 순으로 내림 차순 정렬을 원하기 때문에, 인자로 넘어온 게이머의 점수에서 메서드를 호출하는 게이머의 점수를 빼면 됩니다. 예를 들어 인자로 넘어온 게이머의 점수가 982이고 compareTo() 메서드를 호출하는 객체의 점수가 1018이라면, compareTo() 메서드는 음수(982-1018)를 리턴하게 되며, 이는 곧 메서드를 호출하는 게미머가 인자로 넘어온 게이머보다 작다는 것을 의미합니다. 다시 말해, 메서드를 호출를 호출하는 메서드가 점수가 더 크지만, 객체 자체 비교에서는 인자로 넘어온 게이머보다 작은 객체가 됩니다.

 

Collections.sort(players);
System.out.println(players); // [Player(name=Chloe, score=1090), Player(name=Eric, score=1018), Player(name=Bob, score=982), Player(name=Dale, score=982), Player(name=Alice, score=899)]

 

 

이제 Collections.sort() 메서드에는 Comparable 인터페이스를 구현한 Comparable 타입의 Player 객체의 리스트가 인자로 넘어가기 때문에 더 이상 컴파일 에러가 발생하지 않습니다. 정렬 후 게이머 리스트를 출력해보면 원하는 바와 같이 점수가 제일 높은 Chloe 가 리스트의 맨 앞으로 위치하고, 점수가 제일 낮은 Alice 가 리스트의 맨 뒤에 위치하게 됩니다.

 

 

 

Comparator 객체 사용

만약 정렬 대상 클래스의 코드를 직접 수정할 수 없는 경우에는 어떻게 객체의 정렬 기준을 정의할 수 있을까요? 또는 정렬 하고자 하는 객체에 이미 존재하고 있는 정렬 기준과 다른 정렬 기준으로 정렬을 하고 싶을 때는 어떻게 해야할까요?

이 때 필요한 것이 바로 Comparable 인터페이스와 이름이 유사한 Comparator 인터페이스입니다. Comparator 인터페이스의 구현체를 Arrays.sort()나 Collections.sort()와 같은 정렬 메서드의 추가 인자로 넘기면 정렬 기준을 누락된 클래스의 객체나 기존 정렬 기준을 무시하고 새로운 정렬 기준으로 객체를 정렬할 수 있습니다.

 

 

Comparator<Player> comparator = new Comparator<Player>() {
    @Override
    public int compare(Player a, Player b) {
        return b.getScore() - a.getScore();
    }
};

Collections.sort(players, comparator);
System.out.println(players); // [Player(name=Chloe, score=1090), Player(name=Eric, score=1018), Player(name=Bob, score=982), Player(name=Dale, score=982), Player(name=Alice, score=899)]

 

 

 

위 코드는 Comparator 객체를 Collections.sort() 메서드의 두번째 인자로 넘겨서 이전 섹션과 동일한 정렬 결과를 만들어 내고 있습니다. 이렇게 Comparator 객체를를 인자로 넘기면, 정렬 대상 객체가 Comparable 인터페이스를 구현 여부와 상관없이, 넘어온 Comparator 구현체의 compare() 메서드 기준으로 정렬을 수행합니다. compare() 메서드는 비교 대상 2 개의 객체를 인자를 차례로 인자로 받습니다. 첫번째 인자가 두번째 인자보다 작다면 음수, 같다면 0, 크다면 양수를 리턴하면 됩니다.

 

 

람다 함수로 대체

Comparator 객체는 메서드가 하나 뿐인 함수형 인터페이스를 구현하기 때문에 람다 함수로 대체가 가능합니다.

 

 

Collections.sort(players, (a, b) -> b.getScore() - a.getScore());
System.out.println(players); // [Player(name=Chloe, score=1090), Player(name=Eric, score=1018), Player(name=Bob, score=982), Player(name=Dale, score=982), Player(name=Alice, score=899)]

 

 

 

 

Stream 으로 정렬

Stream 클래스의 sorted() 메서드도 Comparator 객체를 인자로 받아 정렬을 해줍니다. 스트림을 사용하면 위에서 살펴본 배열과 리스트의 정렬과 달리 기존 객체의 순서를 변경하지 않고, 새롭게 정렬된 객체를 생성하고자 할 때 사용됩니다.

 

List<Player> sortedPlayers = players.stream()
        .sorted((a, b) -> b.getScore() - a.getScore())
        .collect(Collectors.toList());
System.out.println(sortedPlayers); // [Player(name=Chloe, score=1090), Player(name=Eric, score=1018), Player(name=Bob, score=982), Player(name=Dale, score=982), Player(name=Alice, score=899)]

 

 

 

ref : https://www.javastudypoint.com/2019/03/comparable-vs-comparator-in-java.html

ref : https://www.daleseo.com/java-comparable-comparator/

ref :https://server-engineer.tistory.com/808

반응형
반응형

JAVA에서 기본적인 자료 구조를 제공하기 위한 환경을 JAVA Collection Framework라고 한다.

다음은 JAVA Collection Framework의 상속 기본 구조이다.

 

 

 

 

 

  1.  Collection 인터페이스를 상속받아 List와 Set 인터페이스가 된다. List는 순서가 있는 Collection, 그리고 List는 Data 중복을 허락한다. 하지만 Set은 순서의 의미가 없으며 Data를 중복해서 포함할 수 없다.
  2. Collection
  • List 인터페이스의 특징
    • 순서가 있는 Collection.(이 순서는 삽입된 순서를 의미한다.)
    • Data를 중복해서 포함할 수 있다.
    • Stack의 특징
      • Data의 삽입과 추출이 후입선출(Last-In First-Out) 구조로 되어 있다.
      • push() method : Data 삽입할 때 사용
      • pop() method : Data 추출할 때 사용
      • peek() method : 추출할 Data를 삭제하지 않고 Data만을 가져 올 때 사용
      • search() method : Stack으로부터 Data를 검색할 때 사용
    • Vector의 특징
      • 자동으로 동기화를 보장해준다.
      • ArrayList에 동기화가 보장되도록 최적화한 클래스이다.
      • JAVA 5.0 이 후로는 AutoBoxing/AutoUnBoxing을 지원한다.
        • AutoBoxing이란? 기본 Data 타입을 Wrapper 클래스형의 객체로 자동으로 변환해주는 기능. AutoUnBoxing은 AutoBoxing의 반대 개념
        • JAVA 1.4까지
Vector v = new Vector();
v.addElement(new Integer(100));
  • JAVA 5.0이후
Vector v = new Vector();
v.addElement(100); // AutoBoxing 발생, 자동으로 Wrapper class인 Integer로 변경
  • addElement() method : Data를 삽입할 때 사용
  • elementAt() method : Data를 추출할 때 사용, Index에 해당하는 객체를 얻어냄
  • size() method : Vector 내에 존재하는 객체의 수를 얻어낼 대 사용
  • insertElementAt() method : Vector 내에 중간 삽입할 때 사용
  • setElementAt() method : Vector 내에 존재하는 Data를 수정할 때 사용
  • indexOf() method : Vector 내에 Data를 검색할 때 사용, Index를 반환
  • contains() method : Data의 존재 유무를 알기 위해 사용.
  • ArrayList의 특징
    • 동기화를 보장해주지 않는다.
    • 배열에 동적 메모리 증가 기능을 구현한 클래스이다.
    • 동기화 지원 방법 : List list = Collections.synchronizeList(new ArrayList(…));
    • add() method : Data 삽입할 때 사용
    • get(0 method : Data 추출할 때 사용
    • toArray() method : ArrayList로부터 배열 얻어낼 때 사용
    • contains() method : Data의 존재 유무를 알기 위해 사용
    • size() method : ArrayList의 요소 개수를 얻어낼 때 사용
  • Set 인터페이스의 특징
    • 집합적인 개념의 Collection
    • 순서의 의미가 없다.
    • Data를 중복해서 포함할 수 없다.
    • HashSet의 특징
      • add() method : Data 삽입할 때 사용
      • next() method : Data 추출할 때 사용
        • HashSet의 Data 추출은 Iterator을 이용하면 된다. Iterator는 Collection내의 모든 Data에 접근할 수 있는 특징이 있다. 그리고 Data의 마지막에 상관하지 않고 검색하기 위한 인터페이스이다. Set의 Iterator() method로 Iterator를 얻어 낼 수 있으며, Iterator의 hasNext() method를 이용해서 Data 끝을 만날 때까지 next() method를 호출해서 Data를 추출할 수 있다.
Iterator<String iter = set.iterator();
while(iter.hasNext()) {
String temp = iter.next();

System.out.print(temp + ", ");
}
  • remove() method : Data를 삭제할 때 사용
  • contains() method : Data의 포함여부를 알기 위해 사용
  • size() method : HashSet의 요소 개수를 얻어낼 때 사용
       
  1. Map
    List와 Set이 순서나 집합적인 개념의 인터페이스라면 Map은 검색의 개념이 가미된 인터페이스이다. Map 인터페이스는 데이터를 삽입할 때 Key와 Value의 형태로 삽입되며, Key를 이용해서 Value를 얻을 수 있다.
  • Hashtable, HashMap의 공통점
    • 내부적으로 모두 Hash 기법을 이용한다.
    • Map 인터페이스를 구현하고 있다.
    • Key와 Value를 이용해서 Data를 관리한다.
  • Hashtable, HashMap의 차이점
    • Hashtable은 동기화가 보장된다.
    • HashMap은 동기화가 보장되지 않는다.
    • HashMap의 동기화 지원 방법 : Map m = Collections.synchronizedMap(New HashMap(…));
  • Hashtable, HashMap과 HashSet과의 관계
    • Hashtable과 HashMap은 둘 다 Map 인터페이스를 구현하고 있다.
    • HashSet은 내부적으로 Hash기법을 사용하지만 Set인터페이스를 구현하고 있다.
  • HashMap
    • 객체 생성 : Map<String, Integer> map = new HashMap<String, Integer>();
    • put() method : Data 삽입할 때 사용
    • get() method : Data를 추출할 때 사용, argument값은 Key를 사용
  • Hashtable
    • 객체 생성 : Hashtable<String, Object> h = new Hashtable<String, Object>();
    • put() method : Data 삽입할 때 사용
    • get() method : Data를 추출할 때 사용, argument값은 Key를 사용

   

  1. Sorted
    Set과 Map 인터페이스를 상속받아 정렬 기능이 추가된 SortedSet과 SortedMap 인터페이스가 된다. 그리고 이들은 각각 TreeSet 클래스와 TreeMap 클래스로 구성된다. TreeSet과 TreeMap은 Set과 Map의 기능을 가지고 있으면서 정렬 기능이 가미되었다는 것이 특징이다.
  • Sorted를 지원하지 않는 클래스
    • HashSet, HashMap
  • Sorted를 지원하는 클래스
    • TreeSet, TreeMap
  • TreeMap
    • Key와 Value로 Data를 관리
    • Key를 기준으로 오름차순으로 정렬된다.
    • Map 인터페이스를 상속한 SortedMap 인터페이스를 구현한 클래스
  • TreeSet
    • Set 인터페이스를 상속한 SortedSet 인터페이스를 구현한 클래스
    • 데이터들이 자동으로 오름차순으로 정렬된다.
  • Comparator
    • TreeSet과 TreeMap은 사용자가 직접 정렬의 방식을 지정할 수 있다.
    • TreeSet과 TreeMap은 정렬을 위한 Comparator 인터페이스를 구현하면 된다.
    • TreeSet에 Data를 집어 넣으면 기본적으로 오름차순(Ascending) 정렬이 되지만 그것도 문자열이나 기본 데이터 타입과 같은 단순한 것에만 해당된다. 이에 사용자가 직접 비교법을 넣어주기 위해 사용하는 것이 Comparator 인터페이스이다.
    • Comparator의 구현 방법 : Comparator 내부에 compare() method를 구현하면 된다.
class Mycomparator<T> implements Comparator<T> {
public int compare(T o1, T o2) {
// 비교방법 구현
}
  • Comparator가 추가된 TreeSet의 생성
TreeSet<Score> tset = new TreeSet<Score>(new MyComparator<Score>());
  • Comparator가 추가된 TreeMap의 생성
TreeMap<Score, String> tset = new TreeMap<Score, String>(new MyComparator<Score>());
  • 일반적인 정렬기능의 사용
    • HashSet이나 HashMap을 정렬 기능이 지원되는 TreeSet이나 TreeMap으로 변환해서 사용
    • HashSet을 이용한 TreeSet 생성
Set<String> set = new HashSet<String>();
...
TreeSet<String> ts = new TreeSet<String>();
ts.addAll(set);
  • HashMap을 이용한 TreeMap 생성
Map<String, Integer> map = new HashMap<String, Integer>();
...
Map<String, Integer> sortedMap = new TreeMap<String, Integer>();
sortedMap.putAll(map);




ref : https://withwani.tistory.com/150

반응형
반응형

property

wrapper to specify read and write variable functionality using functions

 

template <typename Read, typename Write>
decltype(auto) property ( Read&& read_function, Write&& write_function );
template <typename Read>
decltype(auto) property ( Read&& read_function );
template <typename Write>
decltype(auto) property ( Write&& write_function );

 

These set of functions create a type which allows a setter and getter pair (or a single getter, or a single setter) to be used to create a variable that is either read-write, read-only, or write-only. When used during usertype construction, it will create a variable that uses the setter/getter member function specified.

 

#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>

#include "assert.hpp"

#include <iostream>

class Player {
public:
	int get_hp() const {
		return hp;
	}

	void set_hp( int value ) {
		hp = value;
	}

	int get_max_hp() const {
		return hp;
	}

	void set_max_hp( int value ) {
		maxhp = value;
	}

private:
	int hp = 50;
	int maxhp = 50;
};

int main (int, char*[]) {

	std::cout << "=== properties from C++ functions ===" << std::endl;

	sol::state lua;
	lua.open_libraries(sol::lib::base);

	lua.set("theplayer", Player());

	// Yes, you can register after you set a value and it will
	// connect up the usertype automatically
	lua.new_usertype<Player>( "Player",
		"hp", sol::property(&Player::get_hp, &Player::set_hp),
		"maxHp", sol::property(&Player::get_max_hp, &Player::set_max_hp)
	);

	const auto& code = R"(
	-- variable syntax, calls functions
	theplayer.hp = 20
	print('hp:', theplayer.hp)
	print('max hp:', theplayer.maxHp)
	)";

	lua.script(code);

	return 0;
}

 

 

ref : https://sol2.readthedocs.io/en/latest/api/property.html?highlight=sol%3A%3Aproperty#property

반응형
반응형
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
-- 두 개의 연속된 대쉬는 그 대쉬가 있는 한 줄을 주석으로 처리합니다.
 
--[[
     ‘--[[‘는 여러 줄 주석의 시작을 
     ‘--]]’는 여러 줄 주석의 끝을 뜻합니다.
--]]
 
----------------------------------------------------
-- 1. 변수와 흐름 제어.
----------------------------------------------------
 
 
num = 42  -- 모든 수는 double 형입니다.
-- 겁먹지 마십시오, 64 bit double 형에는 정확한 정수 값을 저장할 수 있는 52 bit가 있습니다.
-- 52 bit 보다 작은 크기로 표현할 수 있는 정수에 대해서는 정밀도 문제가 생기지 않습니다.
 
= 'walternate'  -- 파이썬(Python) 같은 바꿀 수 없는 문자열.
= "쌍 따옴표도 쓸 수 있습니다."
= [[ 이중 대괄호는
       여러 줄 문자열의
       시작과 끝을 나타냅니다.]]
= nil  -- t를 정의되지 않은 변수로 만듭니다. 루아에는 가비지 컬렉션 기능이 있습니다.
 
-- 블록은 do와 end로 표기됩니다.
while num < 50 do
  num = num + 1      -- ‘++’ 또는 ’+=’ 연산자는 없습니다.
end
 
-- If 절:
if num > 40 then
  print('over 40')
elseif s ~= 'walternate' then  -- ‘~=’는 같지 않음을 뜻합니다.
  -- 파이썬 같이 같음을 확인하는 연산자는 ‘==’입니다. ‘==’는 문자열에도 사용될 수 있습니다.
  io.write('not over 40\n')  -- 기본적으로 stdout으로 출력됩니다.
else
  -- 변수들은 기본적으로 전역(global) 변수로 만들어집니다.
  thisIsGlobal = 5  -- 변수 이름을 표기할 때는 낙타 등 표기법이 흔히 사용됩니다.
 
  -- 변수를 지역(local) 변수로 만드는 방법:
  local line = io.read()  -- 다음 stdin 줄을 읽습니다.
 
  -- ‘..’ 연산자를 사용하여 문자열 잇기:
  print('Winter is coming, ' .. line)    -- Winter is coming + ‘stdin으로 입력한 문자열’ 출력.
end
 
-- 정의되지 않은 변수들은 nil을 리턴합니다.
-- 이것은 에러가 아닙니다:
foo = anUnknownVariable  -- foo에 anUnknownVariable(한 정의되지 않은 변수)를 넣습니다. 이제 foo = nil.
 
aBoolValue = false
 
-- 불(boolean) 연산에서는 오직 nil과 false만 거짓입니다. 0 과 '' 는 참입니다!
if not aBoolValue then print('twas false'end
 
-- 'or'와 'and'는 short-circuit 됩니다.
-- 다음은 C/자바스크립트에서의 a?b:c 연산자와 비슷합니다.
ans = aBoolValue and 'yes' or 'no'  --> 'no'
 
karlSum = 0
for i = 1100 do  -- 그 범위의 양 끝을 포함합니다.
  karlSum = karlSum + i
end
 
-- "100, 1, -1"를 쓰면 범위를 감소하도록 정할 수 있습니다.
fredSum = 0
for j = 1001-1 do
 
  fredSum = fredSum + j 
 
end
 
-- 일반적으로, 범위는 시작, 끝[, 증가 또는 감소량] 입니다.
 
-- 또 다른 루프 작성법: 
repeat
  print('the way of the future')
  num = num - 1
until num == 0
 
 
----------------------------------------------------
-- 2. 함수.
----------------------------------------------------
 
function fib(n)                        -- 피보나치 수
  if n < 2 then return 1 end
  return fib(n - 2+ fib(n - 1)    -- 재귀 함수 
end
 
-- 함수 안에 정의된 함수(closure)와 이름 없는 함수도 쓸 수 있습니다.
function adder(x)  -- 리턴되는 함수는 adder가 호출될 때 생성됩니다. 그리고 x의 값을 기억합니다.
  return function (y) return x + y end 
end
a1 = adder(9)    -- adder가 처음 호출되었으므로 a1에 9가 들어갑니다.
a2 = adder(36)  -- adder가 처음 호출되었으므로 a2에 36이 들어갑니다.
print(a1(16))     -- a1에는 9가 들어있습니다. 
 
                          -- 거기서 다시 a1 인스턴스 adder를 호출하였으므로, 9+16=25가 출력됩니다.
print(a2(64))     -- a2에는 36이 들어있습니다. 
 
                           -- 거기서 다시 a2 인스턴스 adder를 호출하였으므로, 36+64=100이 출력됩니다.
 
-- 리턴, 함수 호출, 그리고 할당은 모두 리스트로 동작합니다. 그 리스트의 길이는 다를 수도 있습니다.
-- 매치되지 않는 수신자들은 nil로 취급됩니다. 매치되지 않는 전송자들은 버려집니다.
 
x, y, z = 1234
-- x = 1, y = 2, z = 3입니다. 4 는 버려집니다.
 
function bar(a, b, c)
  print(a, b, c)
  return 4815162342
end
 
x, y = bar('zaphod')  --> "zaphod  nil nil"이 출력됩니다.
-- x = 4, y = 8이 할당됩니다. 값 15, 16, 23, 42 는 버려집니다.
 
-- 함수는 first-class입니다, 함수는 지역 또는 전역일 수 있습니다.
-- 다음 두 줄은 같습니다:
function f(x) return x * x end
= function (x) return x * x end
 
-- 그리고 다음 두 줄도 같습니다:
local function g(x) return math.sin(x) end
local g; g = function (x) return math.sin(x) end
-- 'local g' 선언은 g를 자기 참조 가능하게 만듭니다.
 
-- 삼각 함수들은 라디안으로 동작합니다.
 
-- 매개변수에 한 문자열만 들어갈 때는 (함수를 호출할 때) 괄호를 붙이지 않아도 됩니다.:
print 'hello'  -- 잘 동작합니다.
 
 
----------------------------------------------------
-- 3. 테이블.
----------------------------------------------------
 
-- 테이블은 루아의 유일한 합성 자료 구조입니다.
-- 테이블은 연관 배열(associative arrays)입니다.
-- php 배열 또는 자바스크립트 객체와 비슷합니다.
-- 테이블은 리스트로도 사용될 수 있는 해시 참조 사전입니다.
 
-- 테이블을 사전이나 맵(map)으로 사용하기.
 
-- 사전은 기본적으로 문자열 키(key)를 가집니다.
= {key1 = 'value1', key2 = false}
 
-- 문자열 키는 자바스크립트 같은 점 표기를 쓸 수 있습니다.
print(t.key1)  -- 'value1' 출력.
t.newKey = {}  -- 새로운 key/value 쌍 추가.
t.key2 = nil   -- 테이블 t에서 key2 제거.
 
-- 키로 (nil이 아닌) 임의의 표기를 사용할 수도 있습니다.
= {['@!#'= 'qbert', [{}] = 1729, [6.28= 'tau'}
print(u[6.28])  -- "tau" 출력
 
-- 키 매칭은 기본적으로 숫자와 문자열 값으로 수행됩니다.
-- 그러나 테이블은 동질성(identity)에 의해 수행됩니다.
= u['@!#']  -- a = 'qbert'
= u[{}]        -- 1729가 들어갈 것으로 기대했지만, 실제로 들어가는 값은 nil입니다. b = nil. 
-- 이유는 검색이 실패하기 때문입니다.
-- 검색 실패 이유는 우리가 사용한 키가 원래 값을 저장할 때 사용된 것과 같은 객체가 아니기 때문입니다.
-- 그래서 더 이식성 높은 키는 문자열과 숫자입니다.
 
-- 매개변수가 테이블 하나인 함수 호출에서는 괄호가 필요 없습니다.
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'}  -- 'Sonmi~451' 출력.
 
for key, val in pairs(u) do  -- 테이블 반복.
  print(key, val)
end
 
-- _G 는 모든 전역들(globals)을 위한 특별한 테이블입니다.
print(_G['_G'== _G)  -- 'true' 출력.
 
-- 테이블을 리스트 또는 배열로 사용하기.
 
-- 리스트는 암묵적으로 정수형 키를 설정합니다.
= {'value1''value2'1.21'gigawatts'}
for i = 1, #v do  -- #v 는 리스트 v의 크기(size)입니다.
  print(v[i])  -- 인덱스는 1부터 시작합니다.
end
-- 리스트는 실제 타입이 아닙니다. v는 그저 하나의 테이블입니다.
-- 이 테이블은 연속적인 정수 키를 가지며, 리스트로 취급됩니다.
 
 
 
----------------------------------------------------
-- 3.1 메타테이블과 메타메소드
----------------------------------------------------
-- 테이블 하나는 메타테이블 하나를 가질 수 있습니다.
-- 그 메타테이블은 연산자 오버로딩을 제공합니다.
-- 나중에 우리는 어떻게 메타테이블이 자바스크립트의 프로토타입 동작을 지원하는지 볼 것입니다.
 
f1 = {a = 1, b = 2}  -- 분수 a/b를 표현.
f2 = {a = 2, b = 3}
 
-- 이것은 실패할 것입니다. (분수에 대한 덧셈은 루아에 정의되어 있지 않기 때문입니다.)
-- s = f1 + f2
 
metafraction = {}
function metafraction.__add(f1, f2)
  sum = {}
  sum.b = f1.b * f2.b
  sum.a = f1.a * f2.b + f2.a * f1.b
  return sum
end
 
setmetatable(f1, metafraction)
setmetatable(f2, metafraction)
 
= f1 + f2  -- f1의 메타테이블에 있는 __add(f1,f2)를 호출합니다.
 
-- f1, f2는 자바스크립트의 프로토타입과 달리 메타테이블에 키가 없습니다.
-- 그래서 당신은 반드시 그 키들을 getmetatable(f1)과 같이 다시 받아와야 합니다.
-- 그 메타테이블은 루아가 그것에 대해 아는 키를 가진 보통 테이블입니다. __add 같이요.
 
-- 그러나 다음 줄은 실패합니다. 왜냐하면, s에는 메타테이블이 없기 때문입니다.
-- t = s + s
-- 아래 주어진 클래스 같은 패턴들이 이 문제를 해결합니다.
 
-- 메타테이블에서 __index는 (myFavs.animal에 있는 점 처럼) 점 참조를 오버로드합니다.
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal  -- 동작합니다! 메타테이블, 고마워요.
 
-- 직접적 테이블 검색이 실패하면, (검색은) 그 메타테이블의 __index 값을 사용하여 다시 시도됩니다.
-- 그리고 이것이 반복됩니다.
 
-- 한 __index 값은 또한 더 사용자가 원하는 대로 맞춰진(customized) 검색을 위한 함수(테이블, 키)일 수 있습니다.
 
-- (add 같은) __index의 값들은 메타메소드라 불립니다.
-- 메타메소드의 전체 목록입니다. 여기서 a는 메타메소드를 가진 한 테이블입니다.
 
-- __add(a, b)                  for a + b
-- __sub(a, b)                  for a - b
-- __mul(a, b)                  for a * b
-- __div(a, b)                  for a / b
-- __mod(a, b)                  for a % b
-- __pow(a, b)                  for a ^ b
-- __unm(a)                     for -a
-- __concat(a, b)               for a .. b
-- __len(a)                     for #a
-- __eq(a, b)                   for a == b
-- __lt(a, b)                   for a < b
-- __le(a, b)                   for a <= b
-- __index(a, b) <함수 또는 테이블>  for a.b
-- __newindex(a, b, c)          for a.b = c
-- __call(a, ...)               for a(...)
 
 
 
----------------------------------------------------
-- 3.2 클래스 같은 테이블 그리고 상속.
----------------------------------------------------
-- 클래스는 (루아에) 내장되어 있지 않습니다. 클래스는 테이블과 메타테이블을 사용하여 만들어집니다.
 
-- 그 예제와 설명은 아래와 같습니다.
 
Dog = {}                                                     -- 1.
 
function Dog:new()                                  -- 2.
  newObj = {sound = 'woof'}                       -- 3.
  self.__index = self                                    -- 4.
  return setmetatable(newObj, self)         -- 5.
end
 
function Dog:makeSound()                    -- 6.
  print('I say ' .. self.sound)
end
 
mrDog = Dog:new()                                  -- 7.
mrDog:makeSound()  -- 'I say woof'         -- 8.
 
-- 1. Dog는 클래스처럼 동작합니다. 사실 Dog는 테이블입니다.
-- 2. function 테이블이름:함수(...)는 function 테이블이름.함수(self,...)와 같습니다.
--    ‘:’은 단지 함수의 첫 인자에 self를 추가합니다.
--    어떻게 self가 값을 얻는지는 아래 7과 8을 읽으십시오.
-- 3. newObj(새 객체)는 클래스 Dog의 한 인스턴스가 될 것입니다.
-- 4. self = 인스턴스로 될 클래스.
--    흔히 self = Dog입니다, 그러나 상속으로 그것이 바뀔 수 있습니다.
--    우리가 newObj의 메타테이블과 self의 __index를 self로 설정하면,
--    newObj는 self의 함수들을 얻습니다.
-- 5. 기억하세요: setmetatable은 그것의 첫 인자를 리턴합니다.
-- 6. ‘:’는 2처럼 동작합니다. 그러나 이번에는 self가 클래스가 아닌 인스턴스가 되리라고 예상할 수 있습니다.
-- 7. Dog:new()는 Dog.new(Dog)와 같습니다. 그래서 new()에서 self=Dog입니다.
-- 8. mrDog:makeSound()는 mrDog.makeSound(mrDog)와 같습니다. 여기서 self=mrDog입니다.
 
 
----------------------------------------------------
 
-- 상속 예제:
 
LoudDog = Dog:new()                                        -- 1.
 
function LoudDog:makeSound()
  s = self.sound .. ' '                                             -- 2.
  print(s .. s .. s)
end
 
seymour = LoudDog:new()                                -- 3.
seymour:makeSound()  -- 'woof woof woof'      -- 4.
 
-- 1. LoudDog는 Dog의 메소드들과 변수들을 가져옵니다.
-- 2. self는 new()에서 온 ‘sound’ 키를 가집니다. 3을 보십시오.
-- 3. LoudDog:new()는 LoudDog.new(LoudDog)와 같습니다.
--    그리고 LoudDog.new(LoudDog)는 Dog.new(LoudDog)로 변환됩니다. 
 
--    LoudDog에 ‘new’ 키가 없기 때문입니다.
--    그러나 LoudDog는 그 메타테이블에 __index=Dog를 가집니다.
--    결과: seymour의 메타테이블은 LoudDog입니다, 그리고 LoudDog.__index = LoudDog. 
 
--    그래서 seymour.key는 seymour.key, LoudDog.key, Dog.key 중 
 
--    주어진 키를 가진 첫번 째 테이블일 것입니다. 
-- 4. 'makeSound' 키는 LoudDog에서 찾을 수 있습니다.
--     seymour:makeSound()는 LoudDog.makeSound(seymour)와 같습니다.
 
 
 
-- 만약 필요하면, 하위 클래스의 new()는 기본 클래스의 것처럼 만들 수 있습니다.
function LoudDog:new()
  newObj = {}
  -- newObj 설정
  self.__index = self
  return setmetatable(newObj, self)
end
 
 
 
----------------------------------------------------
-- 4. 모듈.
----------------------------------------------------
 
--[[ 저는 이 스크립트의 나머지 부분이 실행 가능한 상태로 남도록 
 
--   이 절(section)을 주석으로 처리하고 있습니다. 
 
 
-- mod.lua 파일이 다음과 같다고 가정합시다.
 
local M = {}
 
local function sayMyName()
  print('Hrunkner')
end
 
function M.sayHello()
  print('Why hello there')
  sayMyName()
end
 
return M
 
 
-- 다른 파일도 mod.lua 파일에 있는 기능을 사용할 수 있습니다.
local mod = require('mod')  -- mod.lua 파일 실행.
 
-- require는 모듈을 포함(include)하게 하는 표준 방법입니다.
-- require는 이렇게 동작합니다. (만약 캐쉬되지 않으면요. '캐쉬되다'의 뜻은 아래에서 다시 설명됩니다.)
local mod = (function ()
  <mod.lua 파일의 내용>
end)()
-- mod.lua는 한 함수에 들어있는 내용처럼 한 지역 변수 mod에 대입됩니다. 
 
-- 그래서 mod.lua 안에 있는 지역 변수와 지역 함수들은 그 함수 밖에서는 보이지 않게됩니다.
 
-- mod는 mod.lua의 M과 같기 때문에, 다음은 동작합니다.
mod.sayHello()  -- Hrunkner에게 안녕이라고 말합니다.
 
-- 지역 함수인 sayMyName은 오직 mod.lua 안에서만 존재하므로, 다음은 틀렸습니다.
mod.sayMyName()  -- 에러
 
-- require의 리턴 값들은 캐쉬되어 require가 여러 번 호출되더라도 한 파일은 한번만 실행됩니다.
-- 예를 들어, mod2.lua가 "print('Hi!')”를 포함한다고 가정합시다.
local a = require('mod2')  -- Hi! 출력
local b = require('mod2')  -- 출력 안함; a=b.
 
-- dofile은 require와 비슷하지만 캐싱을 하지 않습니다.
dofile('mod2.lua')  --> Hi!
dofile('mod2.lua')  --> Hi! (다시 실행합니다.)
 
-- loadfile은 루아 파일을 로드하지만 아직 실행하지는 않습니다.
= loadfile('mod2.lua')  -- mod2.lua를 실행하기 위해서는 f()를 호출해야 합니다.
 
-- loadstring은 문자열을 위한 loadfile입니다.
= loadstring('print(343)')  -- 한 함수를 리턴합니다.
g()  -- 343을 출력합니다; 이 함수가 호출되기 전까지는 아무것도 출력되지 않습니다.
 
--]]
 
----------------------------------------------------
-- 5. 참고 자료
----------------------------------------------------
 
 
--[[
 
 
저는 루아를 배우는 것이 정말 좋았습니다. 그래서 저는 Löve 2D 게임 엔진으로 게임들도 만들 수 있었습니다. 그것이 제가 루아를 공부한 이유입니다. 
 
 
저는 BlackBulletIV's Lua for programmers를 보며 루아 공부를 시작하였습니다. 그다음 Programming in Lua 책을 읽었습니다. 그것이 제가 루아를 공부한 방법입니다. 
 
 
lua-users.org에 있는 Lua short reference를 확인해보는 것도 도움이 될 수 있습니다.
 
 
여기서 다루지 않은 주요 주제들은 표준 라이브러리들입니다:
 * string library
 * table library
 * math library
 * io library
 * os library
 
 
어쨌든, 이 문서 파일 전체는 하나의 유효한 루아 파일입니다. 이 문서를 learn.lua로 저장하고 “lua learn.lua”로 실행해 보십시오! 
 
 
이 글은 tylerneylon.com을 위해 처음 쓰였습니다. 이 글은 또한 github gist로 보실 수도 있습니다. 이 글과 형식은 같지만 다른 언어로 쓰인 글들은 여기에서 보실 수 있습니다. 
 
 
http://learnxinyminutes.com/ 
 
루아와 함께 즐거운 시간 되십시오!
 
--]]
cs

 

 

원문: http://tylerneylon.com/a/learn-lua/

 

Learn Lua in 15 Minutes

 

tylerneylon.com

 

 

ref : https://roboticist.tistory.com/576

반응형
반응형
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
using namespace std;
 
template<typename T>
T f(T a) 
    return a + 1
}
 
 
class A {                            //f 함수 테스트시 A 를 제외하고 생각하면 된다
public:
};
 
template <typename ... Types>
void s1(char cc, Types ... args)
{
 
}
 
template <typename ... Types>
void s1(int cc, Types ... args)
{
 
}
 
template <typename ... Types>
void s1(A cc, Types ... args)
{
 
}
 
template <typename ... Types>
void foo(Types ... args)        //받는건 가변인자로 다 받는데
{
 
    s1(args...);                    //인자 첫번째 타입에 따라 s1 함수가 호출 된다
    //s1(f(args)...);                    //개별 인자들에 +1 을 다 한다음 기반 parameter pack 을 넘긴다
}
 
int main(int argc, char* argv[])
{
    foo('s', A(), 3);
    foo(A(), 's'3);
    foo(12.0f, 3);
    return 0;
}
 
 
cs
반응형

'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글

[Modern C++] std::move , 간단 예시  (0) 2022.03.03
std::forward  (0) 2022.03.03
std::map::emplace_hint  (0) 2021.03.21
C++ 캐스팅 Static, Dynamic cast  (0) 2020.04.27
std::remove_reference  (0) 2019.12.31
반응형

 

hint 는 추가될 곳에 대한 힌트(위치)를 주는 것

 

template <class... Args> iterator emplace_hint (const_iterator position, Args&&... args);

Construct and insert element with hint

Inserts a new element in the map if its key is unique, with a hint on the insertion position. This new element is constructed in place using args as the arguments for the construction of a value_type (which is an object of a pair type).

The insertion only takes place if no other element in the container has a key equivalent to the one being emplaced (elements in a map container are unique).

If inserted, this effectively increases the container size by one.

The value in position is used as a hint on the insertion point. The element will nevertheless be inserted at its corresponding position following the order described by its internal comparison object, but this hint is used by the function to begin its search for the insertion point, speeding up the process considerably when the actual insertion point is either position or close to it.

The element is constructed in-place by calling allocator_traits::construct with args forwarded.

Parameters

positionHint for the position where the element can be inserted.
The function optimizes its insertion time if position points to the element that will follow the inserted element (or to the end, if it would be the last).
Notice that this does not force the new element to be in that position within the map container (the elements in a map always follow a specific order).
const_iterator is a member type, defined as a bidirectional iterator type that points to elements.args

Arguments forwarded to construct the new element (of type pair<const key_type, mapped_type>).
This can be one of:
- Two arguments: one for the key, the other for the mapped value.
- A single argument of a pair type with a value for the key as first member, and a value for the mapped value as second.
- piecewise_construct as first argument, and two additional arguments with tuples to be forwarded as arguments for the key value and for the mapped value respectivelly.
See pair::pair for more info.

 

Return value

If the function successfully inserts the element (because no equivalent element existed already in the map), the function returns an iterator to the newly inserted element.

Otherwise, it returns an iterator to the equivalent element within the container.

Member type iterator is a bidirectional iterator type that points to an element.

 

 

Complexity

Generally, logarithmic in the container size.
Amortized constant if the insertion point for the element is position.

 

 

www.cplusplus.com/reference/map/map/emplace_hint/

반응형
반응형


정적 캐스팅  static_cast  컴파일 타임에 잡아준다
 
 dynamic_cast 실행중에 잡아준다
 
 
static cast 의 경우 값 변환시 값을 유지할려고 한다(반올림 오차는 제외_, 이진수 표기는 달라질수 있음, ex 소수를 정수로 바구는 경우)
 
 컴파일 도중에 해준다 static cast 는 상속 관계만 따져서 상속관계에 있다면 컴파일 됨(실행중에는 문제가 생길수 있음)
 즉 완전 다른 상속관계의 캐스팅은 컴파일 에러를 벹는다
 실행도중 크래시 날수 있음
 변수형 체크 후 베이스 클래스를 파생 클래스로 변환시
 
 
 
 
 
 #include


class Animal
{
public:
Animal(){}

virtual void fn() {

}

private:

};

class Cat : public Animal
{
public:

private:

};

class Dog : public Animal
{
public:

private:

};

class AAA {

};


int main()
{

/*
정적 캐스팅
static_cast  컴파일 타임에 잡아준다

dynamic_cast 실행중에 잡아준다


static cast 의 경우 값 변환시 값을 유지할려고 한다(반올림 오차는 제외_, 이진수 표기는 달라질수 있음, ex 소수를 정수로 바구는 경우)

컴파일 도중에 해준다 static cast 는 상속 관계만 따져서 상속관계에 있다면 컴파일 됨(실행중에는 문제가 생길수 있음)
즉 완전 다른 상속관계의 캐스팅은 컴파일 에러를 벹는다
실행도중 크래시 날수 있음
변수형 체크 후 베이슼 ㅡㄹ래스를 파생 클래스로 변환시

*/


//static cast 로 값을 변할 할 경우 값을 유지하려고 한다 2진수 표기는 달라질 수 있음
int intVal = 30;
float fdd = static_cast(intVal); //컴파일 가능
char dd = static_cast(intVal); //컴파일 가능
//char* dd1 = static_cast<char*>(intVal); //컴파일 에러

Animal* animalCat = new Cat;
Cat* myCat = static_cast<Cat*>(animalCat);

Dog* ps = static_cast<Dog*>(animalCat); //static 은 상속관계만 따지기 때문에 에러가 안난다 animalCat 은 Animal 클래스를 부모로 두고 있는데 Dog 는 Animal 을 상속 받음으로, 그러나 위험함

//AAA* aaa = static_cast<AAA*>(animalCat); //상속관계가 없다면 컴파일 에러

//Dog* psd = dynamic_cast<Dog*>(animalCat); //이때는 컴파일 에러를 벹음

Dog* psd = dynamic_cast<Dog*>(animalCat); //!!!virtual 키워드가 없다면 이때는 컴파일 에러를 벹음
//, virtual 키워드가 있다면 컴파일 성공함, 그리고 null을 반환한다 
//dynamic cast 는 RTTI 가 켜 있어야 작동하는데 이것이 꺼져 있다면 static_cast 와 동일하게 동작한다



int b= 10;
int c = static_cast(b);
//unsigned int* c2 = static_cast(&b); //컴파일 에러
//unsigned int* c2 = reinterpret_cast(&b); //컴파일 가능
//int a = reinterpret_cast(b); //컴파일 에러
//Cat* catP = reinterpret_cast<Cat*>(&b); //컴파일 가능


/*
const_cast 는 (포인터의 상수성)만을 변경하고자 할때 사용한다!!!
값에는 안쓰이는데 값은 어차피 복사 되는거니깐 안전함
변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 만듭니다.

const_cast  로는 형을 바꿀 수는 없고 const 또는 volatile 애트리뷰트를 제거할때 사용한다
*/


//int iii = 30;
//const int ddd= const_cast(iii); //컴파일 에러

const int iii2 = 30;
//int ddd = const_cast(iii2); //컴파일 에러, 포인터가 아님
int* ddd = const_cast<int*>(&iii2); //컴파일 가능
const int* ddd3 = const_cast(&iii2); //컴파일 가능

int* tly = nullptr;
const int* ddd7 = const_cast(ddd); //컴파일 성공 const 를 붙여준다

//Animal* animalCat = new Cat;
//const Cat* myCat2 = static_cast(animalCat); //컴파일 가능
//const Cat* myCat2 = const_cast(animalCat); //const cast 는 형변환을 허용하지 않는다


return 0;
}

반응형
반응형

 

 

Defined in header <type_traits>

template< class T >
struct remove_reference;

(since C++11)

If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T.

Member types

Name Definition
type the type referred by T or T if it is not a reference

Helper types

template< class T >
using remove_reference_t = typename remove_reference<T>::type;

(since C++14)

Possible implementation

Example

true

false

false

true

true

true

 

 

ref : https://en.cppreference.com/w/cpp/types/remove_reference

반응형
반응형

IComparer<T> 제네릭 인터페이스의 구현에 대한 기본 클래스를 제공합니다.



[Serializable] public abstract class Comparer<T> : System.Collections.Generic.IComparer<T>, System.Collections.IComparer





예제

다음 예제에서는 파생 클래스 BoxLengthFirst는 Comparer<T> 로부터 상속 받은 클래스입니다. 

이 comparer 는 타입 Box 의 두 오브젝트를 비교합니다

먼저 길이에 의해 그다음, 높이, 너비에 의해 정렬됩니다. 


Box 클래스는 두 Box 오브젝트들 사이에서 기본 비교를 다루기위해  IComparable<T> 인터페이스를 구현합니다

이 기본 구현은 높이, 길이 그리고 넓이에 의해 정렬됩니다

예제는 차이점을 나타냅니다, 두 비교에서 이 비교는 Box오브젝트 List를 정렬하는 것 

첫번째로 BoxLengthFirst Comparer 를 사용하는 것 그리고 기본 comparer 를 사용하여 비교하는 것입니다


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
using System;
using System.Collections;
using System.Collections.Generic;
 
class Program
{
    static void Main(string[] args)
    {
        List<Box> Boxes = new List<Box>();
        Boxes.Add(new Box(42014));
        Boxes.Add(new Box(121212));
        Boxes.Add(new Box(82010));
        Boxes.Add(new Box(6102));
        Boxes.Add(new Box(284));
        Boxes.Add(new Box(268));
        Boxes.Add(new Box(41220));
        Boxes.Add(new Box(18104));
        Boxes.Add(new Box(24418));
        Boxes.Add(new Box(10416));
        Boxes.Add(new Box(10210));
        Boxes.Add(new Box(6182));
        Boxes.Add(new Box(8124));
        Boxes.Add(new Box(12108));
        Boxes.Add(new Box(1466));
        Boxes.Add(new Box(16616));
        Boxes.Add(new Box(2812));
        Boxes.Add(new Box(4248));
        Boxes.Add(new Box(8620));
        Boxes.Add(new Box(181812));
 
        // Sort by an Comparer<T> implementation that sorts
        // first by the length.
        Boxes.Sort(new BoxLengthFirst());
 
        Console.WriteLine("H - L - W");
        Console.WriteLine("==========");
        foreach (Box bx in Boxes)
        {
            Console.WriteLine("{0}\t{1}\t{2}",
                bx.Height.ToString(), bx.Length.ToString(), 
                bx.Width.ToString());
        }
 
        Console.WriteLine();
        Console.WriteLine("H - L - W"); 
        Console.WriteLine("==========");
 
        // Get the default comparer that 
        // sorts first by the height.
        Comparer<Box> defComp = Comparer<Box>.Default;
 
        // Calling Boxes.Sort() with no parameter
        // is the same as calling Boxs.Sort(defComp)
        // because they are both using the default comparer.
        Boxes.Sort();
 
        foreach (Box bx in Boxes)
        {
            Console.WriteLine("{0}\t{1}\t{2}",
                bx.Height.ToString(), bx.Length.ToString(), 
                bx.Width.ToString());
        }
 
 
        // This explicit interface implementation
        // compares first by the length.
        // Returns -1 because the length of BoxA
        // is less than the length of BoxB.
        BoxLengthFirst LengthFirst = new BoxLengthFirst(); 
 
        Comparer<Box> bc = (Comparer<Box>) LengthFirst;
 
        Box BoxA = new Box(268);
        Box BoxB = new Box(101214);
        int x = LengthFirst.Compare(BoxA, BoxB);
        Console.WriteLine();
        Console.WriteLine(x.ToString());
 
    
 
    }
 
}
 
public class BoxLengthFirst : Comparer<Box> 
{
    // Compares by Length, Height, and Width.
    public override int Compare(Box x, Box y)
    {
        if (x.Length.CompareTo(y.Length) != 0)
        {
            return x.Length.CompareTo(y.Length);
        }
        else if (x.Height.CompareTo(y.Height) != 0)
        {
            return x.Height.CompareTo(y.Height);
        }
        else if (x.Width.CompareTo(y.Width) != 0)
        {
            return x.Width.CompareTo(y.Width);
        }
        else
        {
            return 0;
        }
    }
 
}
 
// This class is not demonstrated in the Main method
// and is provided only to show how to implement
// the interface. It is recommended to derive
// from Comparer<T> instead of implementing IComparer<T>.
public class BoxComp : IComparer<Box>
{
    // Compares by Height, Length, and Width.
    public int Compare(Box x, Box y)
    {
        if (x.Height.CompareTo(y.Height) != 0)
        {
            return x.Height.CompareTo(y.Height);
        }
        else if (x.Length.CompareTo(y.Length) != 0)
        {
            return x.Length.CompareTo(y.Length);
        }
        else if (x.Width.CompareTo(y.Width) != 0)
        {
            return x.Width.CompareTo(y.Width);
        }
        else
        {
            return 0;
        }
    }
}
 
public class Box : IComparable<Box>
{
 
    public Box(int h, int l, int w)
    {
        this.Height = h;
        this.Length = l;
        this.Width = w;
    }
    public int Height { get; private set; }
    public int Length { get; private set; }
    public int Width { get; private set; }
 
    public int CompareTo(Box other)
    {
        // Compares Height, Length, and Width.
        if (this.Height.CompareTo(other.Height) != 0)
        {
            return this.Height.CompareTo(other.Height);
        }
        else if (this.Length.CompareTo(other.Length) != 0)
        {
            return this.Length.CompareTo(other.Length);
        }
        else if (this.Width.CompareTo(other.Width) != 0)
        {
            return this.Width.CompareTo(other.Width);
        }
        else
        {
            return 0;
        }
    }
 
}
 
 




icOMPARER<t> 인터페이스를 상속 받아 구현한 클래스 입니다, 그와 같은 GENERIC 클래스들은 다음과 같은 것들이 있습니다


SortedList<TKey,TValue> and SortedDictionary<TKey,TValue> generic classes.



Remarks

Derive from this class to provide a custom implementation of the IComparer<T> interface for use with collection classes such as the SortedList<TKey,TValue> and SortedDictionary<TKey,TValue> generic classes.

The difference between deriving from the Comparer<T> class and implementing the System.IComparable interface is as follows:

  • To specify how two objects should be compared by default, implement the System.IComparable interface in your class. This ensures that sort operations will use the default comparison code that you provided.

  • To define a comparer to use instead of the default comparer, derive from the Comparer<T> class. You can then use this comparer in sort operations that take a comparer as a parameter.

The object returned by the Default property uses the System.IComparable<T> generic interface (IComparable<T> in C#, IComparable(Of T) in Visual Basic) to compare two objects. If type T does not implement the System.IComparable<T> generic interface, the Default property returns a Comparer<T> that uses the System.IComparable interface.



ref : https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.comparer-1?view=netframework-4.7.2


반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

다익스트라(Dijkstra) 최단경로  (0) 2022.10.27
BFS 길찾기  (0) 2022.10.26
out var  (0) 2018.09.09
dynamic 형식 사용  (0) 2018.09.08
각종 Linq 함수들 Enumerable.Range, Where,Take, Any, Repeat, Distinct, Zip  (0) 2018.09.07
반응형


변환 할경우 변수를 toNum 처럼 만들어야 하는데 이를 좀 더 간결하게

out var 로 대체가 가능하다 C# 7.0 부터..


1
2
3
4
5
6
7
8
9
10
            int toNum;
            if(int.TryParse("1234"out toNum))
            {
                Console.WriteLine(toNum);
            }
 
            if(int.TryParse("1234"out var num))
            {
                Console.WriteLine(num);
            }

cs






out 인수를 사용하여 메서드 호출

C# 6 및 이전 버전에서는 out 인수로 전달하기 전에 별도 문에서 변수를 선언해야 합니다. 

다음 예제에서는 Int32.TryParse 메서드에 전달되기 전에 number라는 변수를 선언합니다. 

이 메서드는 문자열을 숫자로 변환하려고 합니다.

string numberAsString = "1640"; int number; if (Int32.TryParse(numberAsString, out number)) Console.WriteLine($"Converted '{numberAsString}' to {number}"); else Console.WriteLine($"Unable to convert '{numberAsString}'"); // The example displays the following output: // 결과 : Converted '1640' to 1640


C# 7.0부터 별도 변수 선언이 아니라 메서드 호출의 인수 목록에서 out 변수를 선언할 수 있습니다. 

이렇게 하면 보다 간결하고 읽기 쉬운 코드가 생성되며 메서드 호출 전에 실수로 
변수에 값이 할당되는 경우를 방지할 수 있습니다. 

다음 예제는 Int32.TryParse 메서드 호출에서 number 변수를 정의한다는 점을 제외하고 이전 예제와 비슷합니다.

string numberAsString = "1640"; if (Int32.TryParse(numberAsString, out int number)) Console.WriteLine($"Converted '{numberAsString}' to {number}"); else Console.WriteLine($"Unable to convert '{numberAsString}'"); // The example displays the following output: // 결과 : Converted '1640' to 1640




앞의 예제에서 number 변수는 int로 강력하게 형식화됩니다. 

다음 예제와 같이 암시적 형식 지역 변수를 선언할 수도 있습니다.


string numberAsString = "1640"; if (Int32.TryParse(numberAsString, out var number)) Console.WriteLine($"Converted '{numberAsString}' to {number}"); else Console.WriteLine($"Unable to convert '{numberAsString}'"); // The example displays the following output: // 결과 Converted '1640' to 1640




ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/out-parameter-modifier

반응형
반응형


런타임에서 타입 변환이 가능한 Dynamic 형식



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        static void Main(string[] args)
        {
 
            //컴파일 타임에 결정
            var s = "ddd";
 
            //런타임에 데이터 타입이 결정됨
            dynamic dd = 3;
 
            //런타임 중간에 다른 타입으로 변경 가능
            dd = "ddd";
            Console.WriteLine(dd);
 
 
            dd = 10;
 
            //컴파일 타임에 에러가 발생하진 않지만 런타임에서 예외를 발생시킨다
            //Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: '암시적으로 'int' 형식을 'string' 형식으로 변환할 수 없습니다.'
            s = dd;
 
            
        }

cs




dynamic 형식 사용

C# 4에서는 새로운 형식 dynamic을 소개합니다. 이 형식은 정적 형식이지만 dynamic 형식의 개체가 정적 형식 검사를 건너뜁니다.대부분의 경우 이 형식은 object 형식을 가지고 있는 것처럼 작동합니다. 컴파일 시간에 dynamic 형식의 요소는 모든 연산을 지원하는 것으로 간주됩니다. 따라서 개체가 값을 COM API, IronPython 같은 동적 언어, HTML DOM(문서 개체 모델), 리플렉션 또는 프로그램의 다른 곳 등 어디서 가져오든 신경을 쓸 필요가 없습니다. 그러나 코드가 유효하지 않으면 런타임 시 오류가 catch됩니다.

예를 들어 다음 코드의 인스턴스 메서드 exampleMethod1에 매개 변수가 하나뿐인 경우, 컴파일러는 메서드 ec.exampleMethod1(10, 4)에 대한 첫 번째 호출이 유효하지 않음을 인식합니다. 여기에 인수가 두 개 포함되었기 때문입니다. 호출 시 컴파일러 오류가 발생합니다. 컴파일러는 메서드 dynamic_ec.exampleMethod1(10, 4)에 대한 두 번째 호출을 확인하지 않습니다. dynamic_ec의 형식이 dynamic이기 때문입니다. 따라서 컴파일러 오류가 보고되지 않습니다. 그러나 이 오류는 알림을 무기한 이스케이프하지 않고, 런타임에 catch되며 런타임 예외를 일으킵니다.


static void Main(string[] args)
{
    ExampleClass ec = new ExampleClass();
    // The following call to exampleMethod1 causes a compiler error 
    // if exampleMethod1 has only one parameter. Uncomment the line
    // to see the error.
    //ec.exampleMethod1(10, 4);

    dynamic dynamic_ec = new ExampleClass();
    // The following line is not identified as an error by the
    // compiler, but it causes a run-time exception.
    dynamic_ec.exampleMethod1(10, 4);

    // The following calls also do not cause compiler errors, whether 
    // appropriate methods exist or not.
    dynamic_ec.someMethod("some argument", 7, null);
    dynamic_ec.nonexistentMethod();}
class ExampleClass
{
    public ExampleClass() { }
    public ExampleClass(int v) { }

    public void exampleMethod1(int i) { }

    public void exampleMethod2(string str) { }
}

이 예제에서 컴파일러의 역할은 dynamic으로 형식이 지정된 개체 또는 식에 대해 각 문이 해야 할 일에 대한 정보를 패키지하는 것입니다. 런타임에는 저장된 정보의 검사가 수행되며, 유효하지 않은 문에서 런타임 예외가 발생합니다.

대부분의 동적 작업은 결과 그 자체가 dynamic입니다. 다음 예제에서 testSum이 사용된 곳에 마우스 포인터를 올려두면 IntelliSense에서 (지역 변수) dynamic testSum 형식을 표시합니다.


dynamic d = 1;
var testSum = d + 3;
// Rest the mouse pointer over testSum in the following statement.
System.Console.WriteLine(testSum);

결과가 dynamic이 아닌 작업은 다음을 포함합니다.

  • dynamic에서 다른 형식으로의 전환.
  • dynamic 형식의 인수를 포함하는 생성자 호출.

예를 들어 다음 선언에서 testInstance의 형식은 dynamic이 아니라 ExampleClass입니다.


var testInstance = new ExampleClass(d);

변환 예제는 다음 섹션인 "변환"에 나와 있습니다.

변환

동적 개체와 다른 형식 간에 손쉽게 변환할 수 있습니다. 따라서 개발자는 동적 동작과 비동적 동작 간에 전환할 수 있습니다.

다음 예제와 같이 개체를 동적 형식으로 암시적으로 변환할 수 있습니다.


dynamic d1 = 7;
dynamic d2 = "a string";
dynamic d3 = System.DateTime.Today;
dynamic d4 = System.Diagnostics.Process.GetProcesses();

반대로, 암시적 변환을 dynamic 형식의 식에 동적으로 적용할 수 있습니다.

int i = d1;
string str = d2;
DateTime dt = d3;
System.Diagnostics.Process[] procs = d4;

동적 형식의 인수로 오버로드 확인

메서드 호출 내 하나 이상의 인수에 dynamic 형식이 있거나 메서드 호출의 수신자가 dynamic 형식인 경우 오버로드 확인은 컴파일 시간이 아니라 런타임에 발생합니다. 다음 예제에서 액세스 가능한 유일한 exampleMethod2 메서드가 문자열 인수를 사용하도록 정의되는 경우, d1을 인수로서 전송하면 컴파일러 오류는 발생하지 않지만 런타임 예외가 발생합니다. d1의 런타임 형식은 int인데 exampleMethod2에는 문자열이 필요하므로 오버로드 확인이 런타임에 실패합니다.


// Valid.
ec.exampleMethod2("a string");

// The following statement does not cause a compiler error, even though ec is not
// dynamic. A run-time exception is raised because the run-time type of d1 is int.
ec.exampleMethod2(d1);
// The following statement does cause a compiler error.
//ec.exampleMethod2(7);

동적 언어 런타임

DLR(동적 언어 런타임)은 .NET Framework 4의 새로운 API입니다. DLR은 C#에서 dynamic 형식을 지원하는 인프라를 제공하며, IronPython 및 IronRuby와 같은 동적 프로그래밍 언어를 구현합니다. DLR에 대한 자세한 내용은 동적 언어 런타임 개요를 참조하세요.

COM Interop

C# 4에는 Office 자동화 API와 같은 COM API와의 상호 운용 환경을 개선하는 몇 가지 기능이 포함되어 있습니다. 개선 사항 중에는 dynamic 형식의 사용 및 명명된 인수 및 선택적 인수의 사용이 포함됩니다.

많은 COM 메서드는 형식을 object로 지정하여 인수 형식 및 반환 형식의 변환을 허용합니다. C#에서는 강력한 형식의 변수로 조정하기 위해 값을 명시적으로 캐스팅해야 했습니다. /link(C# 컴파일러 옵션) 옵션을 사용하여 컴파일하는 경우 dynamic 형식을 사용하면 COM 서명에서 object의 발생을 마치 dynamic 형식인 것처럼 취급하여 캐스팅을 상당 부분 피할 수 있습니다. 예를 들어 다음 문은 dynamic 형식은 있고 dynamic 형식은 없는 Microsoft Office Excel 스프레드시트의 셀에 액세스하는 방법과 대조됩니다.


// Before the introduction of dynamic.
((Excel.Range)excelApp.Cells[1, 1]).Value2 = "Name";
Excel.Range range2008 = (Excel.Range)excelApp.Cells[1, 1];




// After the introduction of dynamic, the access to the Value property and
// the conversion to Excel.Range are handled by the run-time COM binder.
excelApp.Cells[1, 1].Value = "Name";
Excel.Range range2010 = excelApp.Cells[1, 1];




ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/types/using-type-dynamic




반응형
반응형



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
 
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace TestProject
{
 
    class t1s
    {
 
        static void Main(string[] args)
        {
            //지정된 된 범위 내의 정수 시퀀스를 생성 합니다.
            //Enumerable.Range 시작숫자부터 개수까지 숫자를 만드는 linq 함수
            int[] arr =  Enumerable.Range(110).Select(x => x * x).ToArray();
 
 
 
            foreach (var item in arr.Where(num => num % 2 == 1))  //linq 를 통해 배열에 확장 메소드 Where 를 사용 할 수 있다
            {
                Console.WriteLine(item);
            }
 
            Console.WriteLine("");
 
            foreach (var item in arr.Where(num => num % 2 == 1).Take(3))  //Take 는 앞에서부터 가져올 개수를 지정해준다
            {
                Console.Write(item + " " );
            }
            Console.WriteLine("");
 
 
            Console.WriteLine(   arr.Any(num => num == 1));     //Any : 값이 하나라도 존재하는가? , linq 에 있는 확장 메소드
 
            Console.WriteLine("");
 
 
 
            //3을 5번 생성
            int[] arr2 = Enumerable.Repeat(35).ToArray();
 
            foreach (var item in arr2)
            {
                Console.Write(item);
            }
 
 
            Console.WriteLine("");
            arr2[1]=5;
            arr2[3= 5;
            //중복된 값을 하나로 (중복 제거)
            foreach (var item in arr2.Distinct())
            {
                Console.Write(item+" ");
            }
 
 
            Console.WriteLine("\n\narr ");
            foreach (var item in arr)
            {
                Console.Write(item + " ");
            }
            Console.WriteLine("\n\narr2");
            foreach (var item in arr2)
            {
                Console.Write(item + " ");
            }
            Console.WriteLine("\n");
 
            //zip 두개의 컨테이너를 돌면서 원소가 양쪽 모두 존재할때까지 두 원소를 합친 컨테이너를 리턴
            var t3 = arr.Zip(arr2, (first, second) => "[ " + first + " : " + second + " ]" );
 
            Console.WriteLine("");
 
            foreach (var item in t3)
            {
                Console.Write(item + ",  ");
            }
        }
 
    }
}
 
 
 

cs



결과 


1

9

25

49

81


1 9 25

True


33333

3 5


arr

1 4 9 16 25 36 49 64 81 100


arr2

3 5 3 5 3



[ 1 : 3 ],  [ 4 : 5 ],  [ 9 : 3 ],  [ 16 : 5 ],  [ 25 : 3 ],



반응형
반응형

익명 함수


익명 함수는 대리자 형식이 예상되는 곳에서 항상 사용할 수 있는 “인라인” 문 또는 식입니다. 

이를 사용하여 명명된 대리자를 초기화하거나 명명된 대리자 형식 대신 이를 메서드 매개 변수로 전달할 수 있습니다.

익명 함수에는 두 가지가 있고 각각 다음 항목에서 설명합니다.


C#에서 대리자의 발전


C# 1.0에서는 코드의 다른 위치에 정의된 메서드를 사용하여 명시적으로 초기화하는 방식으로 대리자의 인스턴스를 만들었습니다. 


C# 2.0에서는 대리자 호출에서 실행될 수 있는 이름 없는 인라인 문 블록을 작성하는 방법으로 무명 메서드의 개념을 소개했습니다. 


C# 3.0에서는 개념적으로 무명 메서드와 비슷하지만 더 간결하고 표현이 다양한 람다 식을 소개했습니다. 


이러한 두 기능을 함께 익명 함수라고 합니다. 


일반적으로 .NET Framework의 버전 3.5 이상을 대상으로 하는 응용 프로그램은 람다 식을 사용해야 합니다.



다음 예제에서는 C# 1.0에서 C# 3.0까지 대리자 만들기의 발전을 보여 줍니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Test
{
    delegate void TestDelegate(string s);
    static void M(string s)
    {
        Console.WriteLine(s);
    }
 
    static void Main(string[] args)
    {
        // Original delegate syntax required 
        // initialization with a named method.
        TestDelegate testDelA = new TestDelegate(M);
 
        // C# 2.0: A delegate can be initialized with
        // inline code, called an "anonymous method." This
        // method takes a string as an input parameter.
        TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };


//C# 3.0. 델리게이트가 람다 표현에 의해 초기화 될 수 있고 또한 람다는
//string 입력 파라미터를 받을 수 있는데 x 의 타입은 컴파일러에 의해서 추론됩니다.
        // C# 3.0. A delegate can be initialized with
        // a lambda expression. The lambda also takes a string
        // as an input parameter (x). The type of x is inferred by the compiler.
        TestDelegate testDelC = (x) => { Console.WriteLine(x); };
 
        // Invoke the delegates.
        testDelA("Hello. My name is M and I write lines.");
        testDelB("That's nothing. I'm anonymous and ");
        testDelC("I'm a famous author.");
 
        // Keep console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    Hello. My name is M and I write lines.
    That's nothing. I'm anonymous and
    I'm a famous author.
    Press any key to exit.
 */

cs




ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/statements-expressions-operators/anonymous-functions

반응형
반응형


컴파일 타임에 확장 메서드 바인딩

확장 메서드를 사용하여 클래스 또는 인터페이스를 확장할 수 있지만 재정의할 수는 없습니다. 
(확장 메소드 자체가 static 으로 정의됨)

이름과 시그니처가 인터페이스 또는 클래스 메서드와 동일한 확장 메서드는 호출되지 않습니다. 

컴파일 시간에 확장 메서드는 항상 형식 자체에서 정의된 인스턴스 메서드보다 우선 순위가 낮습니다. 

즉, 형식에 Process(int i)라는 메서드가 있고 동일한 시그니처를 가진 확장 메서드가 있는 경우 컴파일러는 항상 인스턴스 메서드에 바인딩합니다. 컴파일러는 메서드 호출을 발견할 경우 먼저 형식의 인스턴스 메서드에서 일치 항목을 찾습니다. 일치 항목이 없으면 형식에 대해 정의된 확장 메서드를 검색하고 찾은 첫 번째 확장 메서드에 바인딩합니다. 다음 예제에서는 컴파일러가 바인딩할 확장명 메서드 또는 인스턴스 메서드를 확인하는 방법을 보여 줍니다.

다음 예제에서는 C# 컴파일러가 메서드 호출을 형식의 인스턴스 메서드 또는 확장명 메서드에 바인딩할 것인지 결정할 때 따르는 규칙을 보여 줍니다. 정적 클래스 Extensions는 IMyInterface를 구현하는 모든 형식에 대해 정의된 확장 메서드를 포함합니다. AB및 C 클래스는 모두 인터페이스를 구현합니다.

MethodB 확장 메서드는 이름과 시그니처가 클래스에서 이미 구현된 메서드와 정확하게 일치하므로 호출되지 않습니다.

일치하는 시그니처를 가진 인스턴스 메서드를 찾을 수 없으면 컴파일러는 일치하는 확장명 메서드(있는 경우)에 바인딩합니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Define an interface named IMyInterface.
namespace DefineIMyInterface
{
    using System;
 
    public interface IMyInterface
    {
        // Any class that implements IMyInterface must define a method
        // that matches the following signature.
        void MethodB();
    }
}
 
 
// Define extension methods for IMyInterface.
namespace Extensions
{
    using System;
    using DefineIMyInterface;
 
    // The following extension methods can be accessed by instances of any 
    // class that implements IMyInterface.
    
    //확장 메소드를 만들기 위해선 확장 메소드가 포함되는 클래스에 static 을 붙여줘야 한다
    public static class Extension
    {
 
        //각각 IMyInterface 에 대한 확장 메소드 MethodA 에 대하여 정의한다
 
        //확장 메소드의 경우 this 가 붙는데 this 다음에 넘겨줄 타입을 정해줘야한다
        //확장 메소드 또한 오버로드가 가능하다
        public static void MethodA(this IMyInterface myInterface, int i)
        {
            Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, int i)");
        }
 
        public static void MethodA(this IMyInterface myInterface, string s)
        {
            Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, string s)");
        }
 
        //이 함수는 호출되지 않는데 왜냐면 각 A,B,C 3개 클래스는 시그니처에 매칭되는 MethodB를 구현하기 때문이다
        // This method is never called in ExtensionMethodsDemo1, because each 
        // of the three classes A, B, and C implements a method named MethodB
        // that has a matching signature.
        public static void MethodB(this IMyInterface myInterface)
        {
            Console.WriteLine("Extension.MethodB(this IMyInterface myInterface)");
        }
    }
}
 
 
// Define three classes that implement IMyInterface, and then use them to test
// the extension methods.
namespace ExtensionMethodsDemo1
{
    using System;
    using Extensions;
    using DefineIMyInterface;
 
    //각 클래스는 확장 메소드에 의해 확장된 IMyInterface 인터페이스를 상속 받는다
    class A : IMyInterface
    {
        public void MethodB() { Console.WriteLine("A.MethodB()"); }             //MethodB 구현 정의함
    }
 
    class B : IMyInterface
    {
        public void MethodB() { Console.WriteLine("B.MethodB()"); }             //MethodB 구현 정의함
        public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
    }
 
    class C : IMyInterface
    {
        public void MethodB() { Console.WriteLine("C.MethodB()"); }             //MethodB 구현 정의함
        public void MethodA(object obj)
        {
            Console.WriteLine("C.MethodA(object obj)");
        }
    }
 
 
 
 
    public  class DemoClass
    {
        static void Main(string[] args)
        {
            // Declare an instance of class A, class B, and class C.
            // For a, b, and c, call the following methods:
            //      -- MethodA with an int argument
            //      -- MethodA with a string argument
            //      -- MethodB with no argument.
            //      
            A a = new A();
            B b = new B();
            C c = new C();
 
            
 
            // A contains no MethodA, so each call to MethodA resolves to 
            // the extension method that has a matching signature.
            a.MethodA(1);           // Extension.MethodA(IMyInterface, int)
            a.MethodA("hello");     // Extension.MethodA(IMyInterface, string)
 
            // A has a method that matches the signature of the following call
            // to MethodB.
            a.MethodB();            // A.MethodB()
 
            Console.WriteLine("");
 
            // B has methods that match the signatures of the following
            // method calls.
            b.MethodA(1);           // B.MethodA(int)
            b.MethodB();            // B.MethodB()
 
            // B has no matching method for the following call, but 
            // class Extension does.
            b.MethodA("hello");     // Extension.MethodA(IMyInterface, string)
 
 
            Console.WriteLine("");
 
 
            //C의 경우에는 확장 메소드MethodA가 있어도 C클래스에서 object 에 대한 함수를 정의해놨기 때문에
            //클래스(C)의 MethodA 가 호출되게 된다
            // C contains an instance method that matches each of the following
            // method calls.
            c.MethodA(1);           // C.MethodA(object)
            c.MethodA("hello");     // C.MethodA(object)
            c.MethodB();            // C.MethodB()
        }
    }
}
 

cs



결과


Extension.MethodA(this IMyInterface myInterface, int i)

Extension.MethodA(this IMyInterface myInterface, string s)

A.MethodB()


B.MethodA(int i)

B.MethodB()

Extension.MethodA(this IMyInterface myInterface, string s)


C.MethodA(object obj)

C.MethodA(object obj)

C.MethodB()



일반 지침

일반적으로 반드시 필요한 경우에만 드물게 확장 메서드를 구현하는 것이 좋습니다. 

가능하면 기존 형식을 확장해야 하는 클라이언트 코드는 기존 형식에서 파생된 새 형식을 만들어 이 작업을 수행해야 합니다. 
자세한 내용은 상속을 참조하세요.

기존 메서드를 사용하여 소스 코드를 변경할 수 없는 형식을 확장하는 경우 형식의 구현이 변경되어 확장명 메서드가 손상될 수도 있습니다.

지정된 형식에 대해 확장 메서드를 구현하는 경우 다음 사항에 유의하세요.

  • 시그니처가 형식에 정의된 메서드와 동일한 확장 메서드는 호출되지 않습니다.

  • 확장 메서드는 네임스페이스 수준에서 범위로 가져옵니다. 
    예를 들어 Extensions라는 단일 네임스페이스에 확장 메서드를 포함하는 여러 개의 정적 클래스가 있는 경우 using Extensions; 지시문을 통해 모두 범위로 가져옵니다.


구현된 클래스 라이브러리의 경우 어셈블리의 버전 번호가 증가되는 것을 방지하기 위해 확장 메서드를 사용해서는 안 됩니다. 소스 코드를 소유하고 있는 라이브러리에 중요 기능을 추가하려는 경우 어셈블리 버전 관리를 위한 표준 .NET Framework 지침을 따라야 합니다. 자세한 내용은 어셈블리 버전 관리를 참조하세요.



ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/extension-methods



반응형
반응형


확장명 메서드를 사용하면 새 파생 형식을 만들거나 다시 컴파일하거나 원래 형식을 수정하지 않고도 기존 형식에 메서드를 "추가"할 수 있습니다. 확장 메서드는 특수한 종류의 정적 메서드이지만 확장 형식의 인스턴스 메서드인 것처럼 호출됩니다. C#, F# 및 Visual Basic에서 작성된 클라이언트 코드의 경우 확장명 메서드를 호출하는 것과 형식에 실제로 정의된 메서드를 호출하는 데는 명백한 차이가 없습니다.


다음 예제에서는 정수 배열에서 표준 쿼리 연산자 OrderBy를 호출하는 방법을 보여 줍니다. 괄호 안의 식은 람다 식입니다. 많은 표준 쿼리 연산자가 람다 식을 매개 변수로 사용하지만 확장명 메서드에 대한 요구 사항은 아닙니다. 자세한 내용은 람다 식을 참조하세요.



가장 일반적인 확장명 메서드는 쿼리 기능을 존 System.Collections.IEnumerable 및 

System.Collections.Generic.IEnumerable<T>  형식에 추가하는 LINQ 표준 쿼리 연산자입니다.


표준 쿼리 연산자를 사용하려면 using System.Linq 지시문을 사용해서 먼저 범위를 지정합니다. 

그러면 IEnumerable<T>을 구현하는 모든 형식에 GroupByOrderByAverage 등의 인스턴스 메서드가 있는 것처럼 나타납니다. 


List<T> 또는 Array와 같은 IEnumerable<T> 형식의 인스턴스 뒤에 "dot"를 입력하면 IntelliSense 문 완성에서 

이러한 추가 메서드를 볼 수 있습니다.



//상단에 아래 구문 필요

using System.Linq;


class ExtensionMethods2 { static void Main() { int[] ints = { 10, 45, 15, 39, 21, 26 }; var result = ints.OrderBy(g => g); foreach (var i in result) { System.Console.Write(i + " "); } } } //Output: 10 15 21 26 39 45


확장명 메서드는 정적 메서드로 정의되지만 인스턴스 메서드 구문을 사용하여 호출됩니다. 확장 메서드의 첫 번째 매개 변수는 메서드가 작동하는 형식을 지정하며 매개 변수 앞에 this 한정자가 있습니다. 확장 메서드는 using 지시문을 사용하여 명시적으로 네임스페이스를 소스 코드로 가져오는 경우에만 범위에 있습니다.

다음 예제에서는 System.String 클래스에 대해 정의된 확장 메서드를 보여 줍니다. 이 확장 메서드는 제네릭이 아닌 비중첩 정적 클래스 내부에서 정의됩니다.


namespace ExtensionMethods { public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } }


WordCount 지시문을 사용하여 using 확장 메서드를 범위로 가져올 수 있습니다.

using ExtensionMethods;


또한 다음 구문을 사용하여 응용 프로그램에서 확장 메서드를 호출할 수 있습니다.

string s = "Hello Extension Methods"; int i = s.WordCount();


코드에서 인스턴스 메서드 구문을 사용하여 확장 메서드를 호출합니다. 그러나 컴파일러에서 생성된 IL(중간 언어)이 코드를 정적 메서드 호출로 변환합니다. 따라서 실제로 캡슐화의 원칙을 위반하지 않습니다. 사실상 확장명 메서드는 확장하는 형식의 private 변수에 액세스할 수 없습니다.

자세한 내용은 방법: 사용자 지정 확장 메서드 구현 및 호출을 참조하세요.

일반적으로 확장명 메서드를 직접 구현하는 것보다 호출하는 경우가 훨씬 많습니다. 확장 메서드는 인스턴스 메서드 구문을 사용하여 호출되므로 특별한 지식이 없어도 클라이언트 코드에서 확장 메서드를 사용할 수 있습니다. 특정 형식의 확장 메서드를 사용하려면 해당 메서드가 정의된 네임스페이스에 대해 using 지시문을 추가합니다. 예를 들어 표준 쿼리 연산자를 사용하려면 다음 using 지시문을 코드에 추가합니다.

using System.Linq;

System.Core.dll에 대한 참조를 추가해야 할 수도 있습니다. 이제 표준 쿼리 연산자가 대부분의 IEnumerable<T> 형식에 사용할 수 있는 추가 메서드로 IntelliSense에 표시됩니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using MyNamespace;
using System;
 
 
namespace MyNamespace
{
    public static class MyClass
    {
 
        //확장메서드를 사용할 경우 인자에 this 를 써주야 한다
        public static int WordCount(this String str)
        {
            //return str.Split(' ').Length;
            return str.Split(new char[] { ' ''.''?' },
                StringSplitOptions.RemoveEmptyEntries       //공백은 무시
                ).Length;
        }
    }
}
 
class DemoClass
{
    static void Main(string[] args)
    {
        string ss="sdf sdf";
        Console.WriteLine(ss.WordCount());
    }
 
}
 

cs

결과 : 2


ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/extension-methods




반응형
반응형
널 조건부 연산자 ( Evlis[엘비스] 연산자 )

대상?. 연산자는 대상이 null 이면 null 을 리턴하고

대상이 존재하면 대상.멤버 의 값을 리턴하는 연산자입니다


아래는 이에 관한 간단한 예입니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        static void Main(string[] args)
        {
 
            List<int> list = new List<int>();
            list.Add(10);
            //List<int> list = null;
 
            int? numbers = list?.Count;
 
            Console.WriteLine(numbers);   //1
            
        }
 
        static void Main(string[] args)
        {
            //List<int> list = new List<int>();
            //list.Add(10);
            List<int> list = null;
            int? numbers = list?.Count;
            Console.WriteLine(numbers);   //null
            
        }

cs




아래 코드는  null 병합 연산자 ?? 와 혼용한 형태인데

?? 연산자의 경우 ?? 앞의 값이 null 이면 ?? 의 뒤에 값을 리턴하고

?? 앞에 있는 값이 null 이 아니면 앞의 값을 그대로 사용합니다


1
2
3
4
5
6
7
8
9
10
11
        List<string> list = null;
 
        /*
        list = new List<string>();
        list.Add("0");
        list.Add("1");
        list.Add("2");*/
 
        int len = list?.Count ?? -1;
 
        Console.WriteLine(len);
cs


결과 : -1

주석을 해지하면 결과는 3


반응형
반응형


형식 매개 변수에 대한 제약 조건


제약 조건은 형식 인수에서 갖추고 있어야 하는 기능을 컴파일러에 알립니다. 제약 조건이 없으면 형식 인수가 어떤 형식이든 될 수 있습니다. 컴파일러는 모든 .NET 형식의 궁극적인 기본 클래스인 Object의 멤버만 가정할 수 있습니다. 자세한 내용은 제약 조건을 사용하는 이유를 참조하세요. 클라이언트 코드에서 제약 조건에 의해 허용되지 않는 형식을 사용하여 클래스를 인스턴스화하려고 하면 컴파일 시간 오류가 발생합니다. 제약 조건은 where 상황별 키워드를 사용하여 지정됩니다. 다음 표에는 7가지 형식의 제약 조건이 나와 있습니다.

제약 조건설명
where T : struct형식 인수는 값 형식이어야 합니다. Nullable를 제외한 임의의 값 형식을 지정할 수 있습니다. 자세한 내용은 Nullable 형식 사용을 참조하세요.
where T : class형식 인수는 참조 형식이어야 합니다. 이 제약 조건은 모든 클래스, 인터페이스, 대리자 또는 배열 형식에도 적용됩니다.
where T : unmanaged형식 인수는 참조 형식일 수 없으며, 모든 중첩 수준에서 참조 형식 멤버를 포함할 수 없습니다.
where T : new()형식 인수에 매개 변수가 없는 public 생성자가 있어야 합니다. 다른 제약 조건과 함께 사용할 경우 new() 제약 조건을 마지막에 지정해야 합니다.
where T : <기본 클래스 이름>형식 인수가 지정된 기본 클래스이거나 지정된 기본 클래스에서 파생되어야 합니다.
where T : <인터페이스 이름>형식 인수가 지정된 인터페이스이거나 지정된 인터페이스를 구현해야 합니다. 여러 인터페이스 제약 조건을 지정할 수 있습니다. 제약 인터페이스가 제네릭일 수도 있습니다.
where T : UT에 대해 제공되는 형식 인수는 U에 대해 제공되는 인수이거나 이 인수에서 파생되어야 합니다.

일부 제약 조건은 상호 배타적입니다. 모든 값 형식에는 매개 변수가 없는 액세스 가능 생성자가 있어야 합니다. struct 제약 조건은 new() 제약 조건을 의미하고, new() 제약 조건은 struct 제약 조건과 결합할 수 없습니다. unmanaged 제약 조건은 struct 제약 조건을 의미합니다. unmanaged 제약 조건은 struct 또는 new() 제약 조건과 결합할 수 없습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using System;
 
namespace TestProject
{
 
    public interface IIKs
    {
 
    }
 
    public class hy : IIKs
    {
 
        public hy(int s)
        {
 
 
        }
        public hy() => _s = 30;
 
        public override string ToString() => "hy Type !!!!!\t" + _s;
 
        int _s;
    }
 
 
    public class Node<T>
    {
        public Node(T t) => (Next, Data) = (null, t);
 
        public Node<T> Next { get; set; }
 
        T _data;
        public T Data {
            get {
                return _data;
            }
            set {
                _data = value;
            }
        }
    }
 
 
    //T 는 IIKs 를 상속받으면서 기본생성자가 존재해야한다
    public class Car<T> where T : IIKs , new()
    {
        public T ti = new T();
    }
 
 
    class t1s
    {
 
        static void Main(string[] args)
        {
            Car<hy> c = new Car<hy>();
 
            Console.WriteLine(c.ti.ToString());
            
        }
 
    }
}
 

cs


결과 화면
hy Type !!!!!   30

제약 조건을 사용하는 이유

형식 매개 변수 제약을 통해 허용되는 작업 및 메서드 호출 수를 제약 형식 및 해당 상속 계층 구조의 모든 형식에서 지원하는 작업 및 메서드 호출로 늘립니다. 제네릭 클래스 또는 메서드를 디자인할 때 제네릭 멤버에서 단순 할당 이외의 작업을 대해 수행하거나 System.Object에서 지원하지 않는 메서드를 호출하는 경우 형식 매개 변수에 제약 조건을 적용해야 합니다. 예를 들어 기본 클래스 제약 조건은 이 형식의 개체나 이 형식에서 파생된 개체만 형식 인수로 사용된다고 컴파일러에 알립니다. 컴파일러에 이 보장이 있으면 해당 형식의 메서드가 제네릭 클래스에서 호출되도록 허용할 수 있습니다. 다음 코드 예제에서는 기본 클래스 제약 조건을 적용하여 GenericList<T> 클래스(제네릭 소개에 있음)에 추가할 수 있는 기능을 보여 줍니다.

C#
public class Employee
{
    public Employee(string s, int i) => (Name, ID) = (s, i);
    public string Name { get; set; }
    public int ID { get; set; }
}

public class GenericList<T> where T : Employee
{
    private class Node
    {
        public Node(T t) => (Next, Data) = (null, t);        //이 부분은 식 본문 정의글 참고 : http://3dmpengines.tistory.com/2004

        public Node Next { get; set; }
        public T Data { get; set; }
    }

    private Node head;

    public void AddHead(T t)
    {
        Node n = new Node(t) { Next = head };
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;

        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}

이 제약 조건을 통해 제네릭 클래스에서 Employee.Name 속성을 사용할 수 있습니다. 제약 조건은 T 형식의 모든 항목을 Employee개체 또는 Employee에서 상속하는 개체 중 하나로 보장하도록 지정합니다.

동일한 형식 매개 변수에 여러 개의 제약 조건을 적용할 수 있으며, 제약 조건 자체가 다음과 같이 제네릭 형식일 수 있습니다.

C#
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

where T : class 제약 조건을 적용하는 경우 == 및 != 연산자는 참조 ID만 테스트하고 값이 같은지 테스트하지 않으므로 형식 매개 변수에 사용하지 않도록 합니다. 이러한 연산자가 인수로 사용되는 형식에서 오버로드되는 경우에도 이 동작이 발생합니다. 다음 코드는 이 내용을 보여 줍니다. String 클래스가 == 연산자를 오버로드하지만 출력이 false입니다.

C#
public static void OpEqualsTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}
private static void TestStringEquality()
{
    string s1 = "target";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
    string s2 = sb.ToString();
    OpEqualsTest<string>(s1, s2);
}

컴파일러에서 컴파일 시간에 T가 참조 형식이고 모든 참조 형식에 유효한 기본 연산자를 사용해야 한다는 것만 인식합니다. 값 일치 여부를 테스트해야 하는 경우에도 where T : IEquatable<T> 또는 where T : IComparable<T> 제약 조건을 적용하고 제네릭 클래스를 생성하는 데 사용할 모든 클래스에서 인터페이스를 구현하는 것이 좋습니다.

여러 매개 변수 제한

다음 예제와 같이 여러 매개 변수에 제약 조건을 적용하고, 단일 매개 변수에 여러 제약 조건을 적용할 수 있습니다.

C#
class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new()
{ }

바인딩되지 않은 형식 매개 변수

공용 클래스 SampleClass<T>{}의 T와 같이 제약 조건이 없는 형식 매개 변수를 바인딩되지 않은 형식 매개 변수라고 합니다. 바인딩되지 않은 형식 매개 변수에는 다음 규칙이 있습니다.

  • != 및 == 연산자는 구체적인 형식 인수가 이러한 연산자를 지원한다는 보장이 없기 때문에 사용할 수 없습니다.
  • System.Object로/에서 변환하거나 임의의 인터페이스 형식으로 명시적으로 변환할 수 있습니다.
  • null과 비교할 수 있습니다. 바인딩되지 않은 매개 변수를 null과 비교하는 경우 형식 인수가 값 형식이면 비교에서 항상 false를 반환합니다.

제약 조건으로 형식 매개 변수 사용

다음 예제와 같이 고유한 형식 매개 변수가 있는 멤버 함수가 해당 매개 변수를 포함 형식의 형식 매개 변수로 제약해야 하는 경우 제네릭 형식 매개 변수를 제약 조건으로 사용하면 유용합니다.

C#
public class List<T>
{
    public void Add<U>(List<U> items) where U : T {/*...*/}
}

앞의 예제에서 T는 Add 메서드 컨텍스트에서는 형식 제약 조건이고, List 클래스 컨텍스트에서는 바인딩되지 않은 형식 매개 변수입니다.

제네릭 클래스 정의에서 형식 매개 변수를 제약 조건으로 사용할 수도 있습니다. 형식 매개 변수는 다른 형식 매개 변수와 함께 꺾쇠괄호 안에 선언해야 합니다.

C#
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }

컴파일러에서 형식 매개 변수가 System.Object에서 파생된다는 점을 제외하고는 형식 매개 변수에 대해 아무 것도 가정할 수 없기 때문에, 제네릭 클래스에서 형식 매개 변수를 제약 조건으로 사용하는 경우는 제한됩니다. 두 형식 매개 변수 사이의 상속 관계를 적용하려는 시나리오에서 제네릭 클래스에 형식 매개 변수를 제약 조건으로 사용합니다.

관리되지 않는 제약 조건

C# 7.3부터 unmanaged 제약 조건을 사용하여 형식 매개 변수가 관리되지 않는 형식이어야 한다고 지정할 수 있습니다. 관리되지 않는 형식은 참조 형식이 아니며, 모든 중첩 수준에서 참조 형식 필드를 포함하지 않는 형식입니다. unmanaged 제약 조건을 사용하면 다음 예제와 같이 메모리 블록으로 조작할 수 있는 형식을 사용하도록 재사용 가능한 루틴을 작성할 수 있습니다.

C#
unsafe public static byte[] ToByteArray<T>(this T argument) where T : unmanaged
{
    var size = sizeof(T);
    var result = new Byte[size];
    Byte* p = (byte*)&argument;
    for (var i = 0; i < size; i++)
        result[i] = *p++;
    return result;
}

앞의 메서드는 기본 제공 형식으로 알려지지 않은 형식에서 sizeof 연산자를 사용하므로 unsafe 컨텍스트에서 컴파일해야 합니다. unmanaged 제약 조건이 없으면 sizeof 연산자를 사용할 수 없습니다.

대리자 제약 조건

C# 7.3부터 System.Delegate 또는 System.MulticastDelegate를 기본 클래스 제약 조건으로 사용할 수도 있습니다. CLR에서는 항상 이 제약 조건을 허용했지만, C# 언어에서는 이 제약 조건을 허용하지 않았습니다. System.Delegate 제약 조건을 사용하면 형식이 안전한 방식으로 대리자에서 작동하는 코드를 작성할 수 있습니다. 다음 코드는 두 대리자가 동일한 형식인 경우 이를 결합하는 확장 메서드를 정의합니다.

C#
public static TDelegate TypeSafeCombine<TDelegate>(this TDelegate source, TDelegate target)
    where TDelegate : System.Delegate
    => Delegate.Combine(source, target) as TDelegate;

위의 메서드를 사용하여 동일한 형식의 대리자를 결합할 수 있습니다.

C#
Action first = () => Console.WriteLine("this");
Action second = () => Console.WriteLine("that");

var combined = first.TypeSafeCombine(second);
combined();

Func<bool> test = () => true;
// Combine signature ensures combined delegates must
// have the same type.
//var badCombined = first.TypeSafeCombine(test);

마지막 줄의 주석 처리를 제거하면 컴파일되지 않습니다. first과 test는 모두 대리자 형식이지만 서로 다른 대리자 형식입니다.

열거형 제약 조건

C# 7.3부터 System.Enum 형식을 기본 클래스 제약 조건으로 지정할 수도 있습니다. CLR에서는 항상 이 제약 조건을 허용했지만, C# 언어에서는 이 제약 조건을 허용하지 않았습니다. System.Enum을 사용하는 제네릭은 System.Enum의 정적 메서드를 사용하여 결과를 캐시하기 위해 형식이 안전한 프로그래밍을 제공합니다. 다음 샘플에서는 열거형 형식에 유효한 값을 모두 찾은 다음, 해당 값을 문자열 표현에 매핑하는 사전을 작성합니다.

C#
public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

사용된 메서드는 성능에 영향을 주는 리플렉션을 사용합니다. 리플렉션이 필요한 호출을 반복하는 대신, 이 메서드를 호출하여 캐시되고 다시 사용되는 컬렉션을 작성할 수 있습니다.

다음 샘플과 같이 이 메서드는 열거형을 만들고 해당 값과 이름의 사전을 작성하는 데 사용할 수 있습니다.

C#
enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}
C#
var map = EnumNamedValues<Rainbow>();

foreach (var pair in map)
    Console.WriteLine($"{pair.Key}:\t{pair.Value}");



ref :  https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters


반응형
반응형

=> 연산자


연산자는 C#에서 두 가지 방법으로 사용할 수 있습니다.




이 중에서도 두번째 식 본문의 정의에 대해서 알아보겠습니다




식 본문 정의

식 본문 정의는 간결하고 읽을 수 있는 형식으로 멤버 구현을 제공합니다. 다음과 같은 일반적인 구문을 포함합니다.


member => expression;


여기서 expression은 유효한 식입니다. 은 멤버의 반환 형식이 void이거나 멤버가 생성자 또는 종료자인 경우에만 statement 식일 수 있습니다.

메서드 및 속성 가져오기 문에 대한 식 본문 정의는 C# 6부터 지원됩니다. 생성자, 종료자, 속성 설정 문 및 인덱서에 대한 식 본문 정의는 C# 7부터 지원됩니다.

Person.ToString 메서드에 대한 식 본문 정의는 다음과 같습니다.


public override string ToString() => $"{fname} {lname}".Trim();


다음과 같은 메서드 정의의 약식 버전입니다.

public override string ToString() { return $"{fname} {lname}".Trim(); }



식 본문 멤버




식 본문 정의를 사용하면 간결하고 읽을 수 있는 형식으로 멤버 구현을 제공할 수 있습니다. 메서드 또는 속성과 같은 지원되는 멤버에 대한 논리가 단일 식으로 구성된 경우 식 본문 정의를 사용할 수 있습니다. 식 본문 정의의 일반 구문은 다음과 같습니다.


member => expression;


여기서 expression은 유효한 식입니다.

C# 6에서는 메서드 및 속성 가져오기 접근자에 대해 식 본문 정의 지원이 도입되었으며 C# 7.0에서는 지원이 확장되었습니다. 

다음 표에 나열된 형식 멤버와 함께 식 본문 정의를 사용할 수 있습니다.


멤버지원 버전
메서드C# 6
생성자C# 7.0
종료자C# 7.0
속성 가져오기C# 6
속성 설정C# 7.0
인덱서C# 7.0


메서드

식 본문 메서드는 형식이 메서드의 반환 형식과 일치하는 값을 반환하거나 void를 반환하는 메서드의 경우 일부 작업을 수행하는 단일 식으로 구성됩니다. 예를 들어 ToString 메서드를 재정의하는 형식에는 일반적으로 현재 개체의 문자열 표현을 반환하는 단일 식이 포함되어 있습니다.

다음 예제에ToString 메서드를 식 본문 정의로 재정의하는 Person 클래스를 정의합니다. 또한 이름을 콘솔에 표시하는 DisplayName메서드를 정의합니다. return 키워드는 ToString 식 본문 정의에 사용되지 않습니다.


using System; public class Person { public Person(string firstName, string lastName) { fname = firstName; lname = lastName; } private string fname; private string lname; public override string ToString() => $"{fname} {lname}".Trim(); public void DisplayName() => Console.WriteLine(ToString()); } class Example { static void Main() { Person p = new Person("Mandy", "Dejesus"); Console.WriteLine(p); p.DisplayName(); } }



생성자

생성자에 대한 식 본문 정의는 일반적으로 생성자의 인수를 처리하거나 인스턴스 상태를 초기화하는 단일 할당 식 또는 메서드 호출로 구성됩니다.

다음 예제에서는 생성자에 name이라는 단일 문자열 매개 변수가 있는 Location 클래스를 정의합니다. 식 본문 정의에서 Name 속성에 인수를 할당합니다.

public class Location { private string locationName; public Location(string name) => Name = name; public string Name { get => locationName; set => locationName = value; } }


종료자

종료자에 대한 식 본문 정의에는 일반적으로 관리되지 않는 리소스를 해제하는 문 등의 정리 문이 포함되어 있습니다.

다음 예제에서는 식 본문 정의를 사용하여 종료자가 호출되었음을 나타내는 종료자를 정의합니다.

using System; public class Destroyer { public override string ToString() => GetType().Name; ~Destroyer() => Console.WriteLine($"The {ToString()} destructor is executing."); }


속성 가져오기 문

속성 가져오기 접근자를 직접 구현하려는 경우 단순히 속성 값을 반환하는 단일 식에 대해 식 본문 정의를 사용할 수 있습니다. return 문은 사용되지 않습니다.

다음 예제에서는 Location.Name 속성을 정의합니다. 이 속성의 속성 가져오기 접근자는 해당 속성을 지원하는 private locationName필드의 값을 반환합니다.

public class Location { private string locationName; public Location(string name) => Name = name; public string Name { get => locationName; set => locationName = value; } }

식 본문 정의를 사용하는 읽기 전용 속성은 명시적 set 문 없이 구현할 수 있습니다. 사용되는 구문은 다음과 같습니다.

PropertyName => returnValue;


다음 예제에서는 Location 클래스를 정의합니다. 이 클래스의 읽기 전용 Name 속성은 private locationName 필드의 값을 반환하는 식 본문 정의로 구현됩니다.


public class Location { private string locationName; public Location(string name) => locationName = name; public string Name => locationName; }


속성 설정 문

속성 설정 접근자를 직접 구현하려는 경우 속성을 지원하는 필드에 값을 할당하는 한 줄 식에 대해 식 본문 정의를 사용할 수 있습니다.

다음 예제에서는 Location.Name 속성을 정의합니다. 이 속성의 속성 설정 문은 해당 속성을 지원하는 private locationName 필드에 입력 인수를 할당합니다.

public class Location { private string locationName; public Location(string name) => Name = name; public string Name { get => locationName; set => locationName = value; } }


인덱서

속성과 마찬가지로, get 접근자가 값을 반환하는 단일 문으로 구성되거나 set 접근자가 단순 할당을 수행하는 경우 인덱서의 get 및 set 접근자는 식 본문 정의로 구성됩니다.

다음 예제에서는 다양한 스포츠의 이름이 포함된 내부 String 배열을 포함하는 Sports라는 클래스를 정의합니다. 인덱서의 get 및 set 접근자는 둘 다 식 본문 정의로 구현됩니다.

using System; using System.Collections.Generic; public class Sports { private string[] types = { "Baseball", "Basketball", "Football", "Hockey", "Soccer", "Tennis", "Volleyball" }; public string this[int i] { get => types[i]; set => types[i] = value; } }







ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/lambda-operator

ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members

반응형
반응형




전편 : http://3dmpengines.tistory.com/2001?category=509928


다음 예제에서는 abstract 속성을 정의하는 방법을 보여 줍니다. 추상 속성 선언은 속성 접근자의 구현을 제공하지 않습니다. 클래스가 속성을 지원하도록 선언하지만 접근자 구현은 파생 클래스에서 처리되도록 합니다. 다음 예제에서는 기본 클래스에서 상속된 추상 속성을 구현하는 방법을 보여 줍니다.

이 파일에서는 double 형식의 Area 속성을 포함하는 Shape 클래스를 선언합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// compile with: csc -target:library abstractshape.cs
public abstract class Shape
{
    private string name;
 
    public Shape(string s)
    {
        // calling the set accessor of the Id property.
        Id = s;
    }
 
    public string Id
    {
        get
        {
            return name;
        }
 
        set
        {
            name = value;
        }
    }
 
    // Area is a read-only property - only a get accessor is needed:
     public abstract double Area
    {
        get;
    }
 
    public override string ToString()
    {
        return Id + " Area = " + string.Format("{0:F2}", Area);
    }
}

cs


다음 코드에서는 Shape의 세 가지 서브클래스와 이러한 서브클래스에서 Area 속성을 재정의하여 
고유한 구현을 제공하는 방법을 보여 줍니다.



// compile with: csc -target:library -reference:abstractshape.dll shapes.cs public class Square : Shape { private int side; public Square(int side, string id) : base(id) { this.side = side; } public override double Area { get { // Given the side, return the area of a square: return side * side; } } } public class Circle : Shape { private int radius; public Circle(int radius, string id) : base(id) { this.radius = radius; } public override double Area { get { // Given the radius, return the area of a circle: return radius * radius * System.Math.PI; } } } public class Rectangle : Shape { private int width; private int height; public Rectangle(int width, int height, string id) : base(id) { this.width = width; this.height = height; } public override double Area { get { // Given the width and height, return the area of a rectangle: return width * height; } } }




다음 코드에서는 많은 Shape 파생 개체를 만들고 해당 영역을 출력하는 테스트 프로그램을 보여 줍니다.

// compile with: csc -reference:abstractshape.dll;shapes.dll shapetest.cs class TestClass { static void Main() { Shape[] shapes = { new Square(5, "Square #1"), new Circle(3, "Circle #1"), new Rectangle( 4, 5, "Rectangle #1") }; System.Console.WriteLine("Shapes Collection"); foreach (Shape s in shapes) { System.Console.WriteLine(s); } } }

/* Output: Shapes Collection Square #1 Area = 25.00 Circle #1 Area = 28.27 Rectangle #1 Area = 20.00 */



이 샘플은 개별적으로 컴파일된 파일 3개로 구성되었으며, 결과로 생성된 어셈블리는 다음 컴파일 시 참조됩니다.

  • abstractshape.cs: 추상 Area 속성이 포함된 Shape 클래스입니다.

  • shapes.cs: Shape 클래스의 서브클래스입니다.

  • shapetest.cs: 일부 Shape 파생 개체의 영역을 표시할 테스트 프로그램입니다.

예제를 컴파일하려면 다음 명령을 사용합니다.

csc abstractshape.cs shapes.cs shapetest.cs

그러면 shapetest.exe 실행 파일이 생성됩니다.


ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/how-to-define-abstract-properties

반응형
반응형

abstract 키워드를 사용하면, 불완전하여 파생 클래스에서 구현해야 하는 클래스 및 클래스 멤버를 만들 수 있습니다.

sealed 키워드를 사용하면, 이전에 virtual로 표시되었던 클래스나 특정 클래스 멤버의 상속을 방지할 수 있습니다.


추상 클래스 및 클래스 멤버

클래스 정의 앞에 abstract 키워드를 배치하여 클래스를 추상으로 선언할 수 있습니다. 예:


public abstract class A { // Class members here. }


추상 클래스는 인스턴스화할 수 없습니다. 

추상 클래스의 목적은 여러 파생 클래스에서 공유할 수 있는 기본 클래스의 공통적인 정의를 제공하는 것입니다. 

예를 들어 클래스 라이브러리에서 여러 자체 함수에 매개 변수로 사용되는 추상 클래스를 정의한 다음 해당 라이브러리를 사용하는 프로그래머가 파생 클래스를 만들어 클래스의 고유 구현을 제공하도록 할 수 있습니다.

추상 클래스에서는 추상 메서드도 정의할 수 있습니다. 

메서드의 반환 형식 앞에 abstract 키워드를 추가하면 추상 메서드가 정의됩니다. 예:

public abstract class A { public abstract void DoWork(int i); }

추상 메서드에는 구현이 없으므로 메서드 정의 다음에는 일반적인 메서드 블록 대신 세미콜론이 옵니다. 

추상 클래스의 파생 클래스에서는 모든 추상 메서드를 구현해야 합니다. 

추상 클래스에서 기본 클래스의 가상 메서드를 상속하는 경우 추상 클래스에서는 추상 메서드를 사용하여 가상 메서드를 재정의할 수 있습니다. 예:

// compile with: -target:library public class D { public virtual void DoWork(int i) { // Original implementation. } } public abstract class E : D { public abstract override void DoWork(int i); } public class F : E { public override void DoWork(int i) { // New implementation. } }

virtual 메서드는 abstract로 선언되어도 추상 클래스에서 상속된 모든 클래스에 대해 여전히 가상입니다. 

추상 메서드를 상속하는 클래스에서는 메서드의 원본 구현에 액세스할 수 없습니다. 

앞의 예제에서 F 클래스의 DoWork에서는 D 클래스의 DoWork를 호출할 수 없습니다. 

따라서 추상 클래스는 파생 클래스에서 가상 메서드에 대한 새 메서드 구현을 반드시 제공하도록 제한할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
public class F : E
{
    public override void DoWork(int i)
    {
        // New implementation.
        base.DoWork(i); //error
    }
 
}
 

cs

이렇게 호출 할 경우 "base.DoWork(i); 상태 오류 CS0205 추상 기본 멤버를 호출할 수 없습니다."

 


다른 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
 
 //interface 는 구현이나 멤버변수를 넣지 못한다
//interface 에 함수를 추가할 수 있는데 정의와 접근제어자는 빠진 상태로 들어갈 수 있고
//정의는 상속 받는 클래스에서 재정의해야한다
public interface ISO
{
    void show();
    int ret(char s);
 
}
 
public abstract class A : ISO
{
     public A()
    {
 
    }
    public A(int a)
    {
    }
 
    public abstract void left();
    public abstract int ret(char s);
    public abstract void show();
 
    public void right()
    {
 
    }
 
    
}
public class B : A
{
    public B() : this(3)
    {
 
    }
 
    public B(int ss)
    {
        System.Console.WriteLine(ss);
    }
 
    public override void left()
    {
        base.right();
    }
 
    public override int ret(char s)
    {
        throw new System.NotImplementedException();
    }
 
    public override void show()
    {
        throw new System.NotImplementedException();
    }
}
 
 
class MainClass
{
    static void Main()
    {
        B ai = new B();
        ai.left();
    }
}
 
 
 
//결과 
//3
 
 

cs



봉인 클래스 및 클래스 멤버

클래스 정의 앞에 sealed 키워드를 배치하여 클래스를 sealed로 선언할 수 있습니다. 예:


public sealed class D { // Class members here. }




  • 봉인 클래스는 기본 클래스로 사용할 수 없습니다. 

  • 그러므로 추상 클래스가 될 수도 없습니다. 

  • 봉인 클래스는 상속할 수 없습니다.


 봉인 클래스는 기본 클래스로 사용될 수 없으므로 일부 런타임 최적화에서는 봉인 클래스 멤버 호출이 약간 더 빨라집니다.

기본 클래스의 가상 멤버를 재정의하는 파생 클래스의 메서드, 인덱서, 속성 또는
이벤트는 해당 멤버를 봉인으로 선언할 수 있습니다.


이렇게 하면 이후에 파생되는 클래스에서는 해당 멤버가 가상이 아니게 됩니다. 

클래스 멤버 선언에서 override 키워드 앞에 sealed키워드를 배치하면 됩니다. 예:


public class D : C { public sealed override void DoWork() { } }




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// compile with: -target:library
public class D
{
    public virtual void DoWork(int i)
    {
        // Original implementation.
    }
}
 
public abstract class E : D
{
    public sealed override void DoWork(int i)
    {
 
    }
}
 
public class F : E
{
 
    public /* new */  void DoWork(int i)
    {
 
    }
}
 
 
class MainClass
{
    static void Main()
    {
        D di = new D();
        F fi = new F();
        di.DoWork(3);
        fi.DoWork(3);
 
    }
}
 
 
/* 결과
경고    CS0114    'F.DoWork(int)'은(는) 상속된 'E.DoWork(int)' 멤버를 숨깁니다.
현재 멤버가 해당 구현을 재정의하도록 하려면 override 키워드를 추가하세요. 
그렇지 않으면 new 키워드를 추가하세요.
*/

cs




ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/abstract-and-sealed-classes-and-class-members


반응형
반응형



implicit 키워드를 통해 int i = 30;  과 같은 형태를 만들 수 있습니다 

(그렇지만 잘 안쓰입니다 일반적인 new Class 형태를 많이 사용하기 때문에..)


implicit


implicit 키워드는 암시적 사용자 정의 형식 변환 연산자를 선언하는 데 사용됩니다. 변환 시 데이터가 손실되지 않는 경우 이 키워드를 통해 사용자 정의 형식과 다른 형식 간에 암시적 변환을 사용할 수 있습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using System;
using System.Collections.Generic;
using System.Text;
 
namespace TestProject
{
    public class Hello
    {
 
        public int ddd = 30;
        public double val;
 
        public Hello(double d) { val = d; }
 
        public Hello(int data = 0)
        {
            ddd = data;
        }

//오버로딩이 가능하다
        public static implicit operator Hello(int data)
        {
            return new Hello(data);
        }
 
        // User-defined conversion from Digit to double
        public static implicit operator double(Hello d)
        {
            return d.val;
        }
        //  User-defined conversion from double to Digit
        public static implicit operator Hello(double d)
        {
            return new Hello(d);
        }
 
    }
 
    class test
    {
        static void Main(string[] args)
        {
 
//이처럼 바로 대입이 가능 =>
//public static implicit operator Hello(int data) 가 호출됨
            Hello ins = 130;
 
 
            Hello dig = new Hello(7.0);
            //This call invokes the implicit "double" operator
            double num = dig;
            //This call invokes the implicit "Digit" operator
            Hello dig2 = 12.0;
            Console.WriteLine("num = {0} dig2 = {1}   , {2}", num, dig2.val, ins.ddd);
            Console.ReadLine();
 
 
        }
    }
}
 

cs



결과 


num = 7 dig2 = 12   , 130





암시적 변환은 불필요한 캐스트를 제거하여 소스 코드 가독성을 향상할 수 있습니다. 


그러나 암시적 변환 시 프로그래머가 명시적으로 형식 간에 캐스팅할 필요가 없으므로 예기치 않은 

결과를 방지하기 위해 주의해야 합니다. 


일반적으로 암시적 변환 연산자는 예외를 throw하지 않고 정보가 손실되지 않으므로 

프로그래머에게 알리지 않고 안전하게 사용할 수 있습니다. 


변환 연산자가 이러한 조건에 맞지 않는 경우 explicit로 표시되어야 합니다. 

자세한 내용은 변환 연산자 사용을 참조하세요.





ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/implicit

반응형
반응형



방법: ToString 메서드 재정의



C#의 모든 클래스 또는 구조체는 Object 클래스를 암시적으로 상속합니다. 

따라서 C#의 모든 개체는 해당 개체의 문자열 표현을 반환하는 ToString 메서드를 가져옵니다. 

예를 들어 int 형식의 모든 변수에는 해당 내용을 문자열로 반환할 수 있도록 하는 ToString 메서드가 있습니다.

int x = 42;
string strx = x.ToString();
Console.WriteLine(strx);
// Output:
// 42

사용자 지정 클래스 또는 구조체를 만들 때 해당 형식에 대한 정보를 클라이언트 코드에 제공하려면

ToString 메서드를 재정의해야 합니다.

클래스 또는 구조체의 ToString 메서드를 재정의하려면

  1. 다음 한정자 및 반환 형식으로 ToString 메서드를 선언합니다.

    public override string ToString(){}  
    
  2. 문자열을 반환하도록 메서드를 구현합니다.

    다음 예제에서는 클래스의 특정 인스턴스와 관련된 데이터뿐 아니라 클래스의 이름을 반환합니다.

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public override string ToString()
        {
            return "Person: " + Name + " " + Age;
        }
    }
    

    다음 코드 예제와 같이 ToString 메서드를 테스트할 수 있습니다.

    Person person = new Person { Name = "John", Age = 12 };
    Console.WriteLine(person);
    // Output:
    // Person: John 12




ToString 메서드와 함께 형식 문자열 및 다른 형식의 사용자 지정 서식을 사용하는 방법에 대한 자세한 내용은 형식 서식 지정을 참조하세요.

중요

이 메서드를 통해 제공할 정보를 결정하는 경우 신뢰할 수 없는 코드에서 클래스 또는 구조체가 사용될지 여부를 고려합니다. 악성 코드에서 악용될 수 있는 정보를 제공하지 않으면 주의해야 합니다.



ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/how-to-override-the-tostring-method


반응형
반응형

new 와 override 차이


new 로 재정이 할 경우 업 케스팅시 부모의 것이 호출되지만 override 키워드로 재정의 하게 되면

자식 클래스의 함수가 호출됩니다


yo~!


다음 예제에서는 다른 컨텍스트의 비슷한 동작을 보여 줍니다. 이 예제에서는 Car라는 기본 클래스 하나와 이 클래스에서 파생된 두 클래스 ConvertibleCar 및 Minivan을 정의합니다. 기본 클래스에는 DescribeCar 메서드가 포함되어 있습니다. 이 메서드는 자동차에 대한 기본 설명을 표시한 다음 ShowDetails를 호출하여 추가 정보를 제공합니다. 세 클래스는 각각 ShowDetails 메서드를 정의합니다. new 한정자는 ConvertibleCar 클래스의 ShowDetails를 정의하는 데 사용됩니다. override 한정자는 Minivan 클래스의 ShowDetails를 정의하는 데 사용됩니다.




TestCars1은 다음 출력을 생성합니다. 특히 예상과 다를 수 있는 car2의 결과를 확인합니다. 개체 형식은 ConvertibleCar이지만 DescribeCar는 ConvertibleCar 클래스에 정의된 ShowDetails 버전에 액세스하지 않습니다. 해당 메서드는 override 한정자가 아니라 new 한정자로 선언되기 때문입니다. 결과적으로 ConvertibleCar 개체는 Car 개체와 동일한 설명을 표시합니다. Minivan 개체인 car3의 결과와 비교합니다. 이 경우 Minivan 클래스에서 선언된 ShowDetails 메서드가 Car 클래스에서 선언된 ShowDetails 메서드를 재정의하고, 표시되는 설명은 미니밴에 대해 설명합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace OverrideAndNew2
{
 
    // Define the base class, Car. The class defines two virtual methods,  
    // DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
    // class also defines a ShowDetails method. The example tests which version of  
    // ShowDetails is used, the base class method or the derived class method.  
    class Car
    {
        public virtual void DescribeCar()
        {
            System.Console.WriteLine("Four wheels and an engine.");
            ShowDetails();
        }
 
        public virtual void ShowDetails()
        {
            System.Console.WriteLine("Standard transportation.");
        }
    }
 
    // Define the derived classes.  
 
    // Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
    // hides the base class method.  
    class ConvertibleCar : Car
    {
        public new void ShowDetails()
        {
            System.Console.WriteLine("A roof that opens up.");
        }
    }
 
    // Class Minivan uses the override modifier to specify that ShowDetails  
    // extends the base class method.  
    class Minivan : Car
    {
        public override void ShowDetails()
        {
            System.Console.WriteLine("Carries seven people.");
        }
    }
 
    class Program
    {
 
        public static void TestCars1()
        {
            System.Console.WriteLine("\nTestCars1");
            System.Console.WriteLine("----------");
 
            Car car1 = new Car();
            car1.DescribeCar();
            System.Console.WriteLine("----------");
 
            // Notice the output from this test case. The new modifier is  
            // used in the definition of ShowDetails in the ConvertibleCar  
            // class.    
            ConvertibleCar car2 = new ConvertibleCar();
            car2.DescribeCar();
            System.Console.WriteLine("----------");
 
            Minivan car3 = new Minivan();
            car3.DescribeCar();
            System.Console.WriteLine("----------");
        }
 
 
        public static void TestCars2()
        {
            System.Console.WriteLine("\nTestCars2");
            System.Console.WriteLine("----------");
 
            var cars = new List<Car> { new Car(), new ConvertibleCar(), new Minivan() };
 
            foreach (var car in cars)
            {
                car.DescribeCar();
                System.Console.WriteLine("----------");
            }
        }
 
        public static void TestCars3()
        {
            System.Console.WriteLine("\nTestCars3");
            System.Console.WriteLine("----------");
            ConvertibleCar car2 = new ConvertibleCar();
            Minivan car3 = new Minivan();
            car2.ShowDetails();
            car3.ShowDetails();
        } 
 
        public static void TestCars4()
        {
            System.Console.WriteLine("\nTestCars4");
            System.Console.WriteLine("----------");
            Car car2 = new ConvertibleCar();
            Car car3 = new Minivan();
            car2.ShowDetails();
            car3.ShowDetails();
        }
 
        public static void Main(string[] args)
        {
            TestCars1();
 
            TestCars2();
 
            TestCars3();
 
            TestCars4();
        }
 
    }
 
 
 
}
 

cs


결과
TestCars1
----------
Four wheels and an engine.
Standard transportation.
----------
Four wheels and an engine.
Standard transportation.
----------
Four wheels and an engine.
Carries seven people.
----------
TestCars2
----------
Four wheels and an engine.
Standard transportation.
----------
Four wheels and an engine.
Standard transportation.
----------
Four wheels and an engine.
Carries seven people.
----------
TestCars3
----------
A roof that opens up.
Carries seven people.
TestCars4
----------
Standard transportation.
Carries seven people.



ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords


반응형

+ Recent posts