기본 인터페이스 메서드 탐구 (시행착오)

C# 8에 추가된 기본 인터페이스 메서드는 인터페이스 정의가 업데이트 됨에 따라 하위 호환을 유지하면서 기능을 확장할 수 있도록 해줍니다.

C# 8이 나올 때 기능적으로는 대략 알고는 있었지만 딱히 활용할 곳이 없어서 잊고 있다가 오늘 이렇게 활용할 수 있겠구나 싶어서 간단히 테스트 코드를 구현해보았습니다.

컨셉은, View의 object 타입의 DataContext를 기본 인터페이스 메서드를 이용해 ViewModel로 캐스팅 된 것을 바로 쓰자는 것이였는데, 결과적으로는 실패했습니다. 한번 코드를 보시죠.

var view = new SomethingView();
// view.ViewModel = new SomethingViewModel(); // ViewModel에 접근이 안됨

interface IHaveViewModel<T>
    where T : class
{
    object? DataContext { get; set; }

    public T? ViewModel { get => DataContext as T; set => DataContext = value; }
}


abstract class View
{
    public object? DataContext { get; set; }
}

class SomethingView : View, IHaveViewModel<SomethingViewModel>
{
}

class SomethingViewModel
{
    public int Value { get; set; }
}

안타깝게도? 인터페이스에서 구현된 ViewModel은 해당 인터페이스로만 접근이 되도록 제한이 되어 있습니다. 그래서 ViewModel을 호출할 수 없는데요, 이것을 굳이 캐스팅 해서 쓰자 하자면, 배보다 배꼽이 더 커집니다.

(view as IHaveViewModel<SomethingViewModel>).ViewModel = new SomethingViewModel(); // 오! 이건 아닌것 같아요

어짜피 기본 인터페이스 메서드의 기능은 인터페이스 관점에서의 유익함이기 때문에 인터페이스로만 접근되도록 한 것은 올바른 선택이긴 한 것 같습니다만… 이거 어디다가 써먹을 수 있을까요?

결국엔 중간 View 계층을 둬서 ViewModel 인스턴스에 접근이 되도록 하는게 제가 필요한 기능을 구현하는 목적에는 부합됩니다.

var view = new SomethingView();
view.ViewModel = new SomethingViewModel(); // 이렇게;;


abstract class View
{
    public object? DataContext { get; set; }
}

abstract class ExtandView<T> : View
    where T : class
{
    public T? ViewModel { get => DataContext as T; set => DataContext = value; }
}

class SomethingView : ExtandView<SomethingViewModel>
{
}

class SomethingViewModel
{
    public int Value { get; set; }
}

관련해서 좋은 아이디어나 생각 댓글로 공유 부탁해요~

좋아요 2

제가 원하는 기능을 중간 클래스를 만들지 않고 구현하는 다른 방법은 확장 메소드를 이용하는 것입니다.

var view = new SomethingView();
//view.ViewModel = new SomethingViewModel(); // 요렇게는 안되지만,
view.SetViewModel(new SomethingViewModel()); // 요렇게 설정하고
var vm = view.GetViewModel(); // 요렇게 읽을 수 있습니다.

interface IHaveViewModel<T>
    where T : class
{
    object? DataContext { get; set; }

 //   public T? ViewModel { get => DataContext as T; set => DataContext = value; }
}


abstract class View
{
    public object? DataContext { get; set; }
}

class SomethingView : View, IHaveViewModel<SomethingViewModel>
{
}

class SomethingViewModel
{
    public int Value { get; set; }
}

static class ViewExtension
{
    public static T? GetViewModel<T>(this IHaveViewModel<T> view) where T : class => view.DataContext as T;
    public static void SetViewModel<T>(this IHaveViewModel<T> view, T viewmodel) where T : class => view.DataContext = viewmodel;
}

확장메소드의 단점? 이라면 속성은 안되어서 위에 코드처럼 메소드로 표현만 가능하다는 점입니다;

좋아요 3

꼭 인터페이스로만 해야 하나요 ?
ㅋ 저는 아래가 딱 저의 한계 입니다.

    public abstract class ViewModel<T>
    {
        public T DataContext { get; set; }
    }

    public class SomethingView<T> : ViewModel<T>
    {
    }

    public class SomethingViewModel
    {
        public int Value { get; set; }
    }

다른 의도하는 게 있는 것 같은데 어렵네요.

좋아요 2

의도라기 보다는 제가 모르는 방법을 확인하고 싶어서 였습니다.

기본 인터페이스 메서드는 기존 인터페이스를 구현 클래스의 수정 없이 버젼을 올릴 수 있다고 문서에서는 소개하고 있는데요,

러스트는 인터페이스와 유사한 트레잇을 통해 상속을 사용하지 않고도 유사한 구현을 할수가 있어서 이와 비슷한 기본 인터페이스 메소드에 관심이 생겼더랬습니다.

그런데 막상 써보니 명시적 인터페이스 처럼 동작해서 아쉬웠는데 개념적으로는 C#에서는 이래야 맞긴 하더라고요

좋아요 2