2012-07-12 2 views
3

다음 클래스 계층 구조가 있습니다.암시 적 연산자 및 클래스 계층 구조

public abstract class ResourceBase { } 

public abstract class WebResourceBase : ResourceBase { 
    public ResourceBase LocalPath { get; set; } 
    public ResourceBase FtpPath { get; set; } 
} 

public class JavaScript : WebResourceBase { } 

내가하고 싶은 것은 그렇게 선언하고 싶습니다.

new JavaScript() { 
    LocalPath = "/path/goes/here/1.js", 
    FtpPath = "ftp://path/goes/here/1.js" 
} 

확실한 대답은 여기에 암시 적 연산자를 사용하는 것입니다하지만 문제는 내가 너무 LocalPathFtpPath 유형 JavaScript이 될 것이라고 선언 형과 동일 이러한 속성에 파생 유형을 할당 할 것입니다.

나는 내 솔루션이 내가 지금 가지고있는 것보다 더 유연 할 수 있기를 바랍니다. 이 코드는 내 스킨을 크롤링하게 만듭니다. 리플렉션을 사용하는 방법이 있었으면 좋겠고 StackTrace 클래스를 사용했지만 운이없는 정보를 찾으려고했습니다. 어떤 도움을 주시면 감사하겠습니다. 감사.

public abstract class ResourceBase { 
    public static implicit operator ResourceBase(string path) { 
      if (path.EndsWith(".js")) 
       return new JavaScript(path); 
      // etc... 
    } 
} 
+0

'WebResourceBase'는'ResourceBase'에서 파생되어야합니까? – eyossi

+0

암시 적 연산자를 사용하지 않고이를 매핑하는 명시 적 함수를 구현했습니다. 이것은 프로젝트의 수명 동안 코드를 유지하고 이해하는 것을 더욱 어렵게 만들 것입니다. –

+0

@Vince 죄송합니다. 지금 문제가 있습니다. 나는 내 대답을 고칠 것이다. –

답변

3

WebResourceBase 실제로 ResourceBase을 상속하는 것을 의미한다고 가정합니다.

암시 적 연산자를 더 멋지게 만들 수는 없지만 불행히도 여기서는 제네릭이 작동하지 않습니다.


대안 : 제네릭 종류의 (하나의 옵션은 파생 클래스를 참조하는 일반적인 매개 변수를 포함하는 클래스를 수정하는 것입니다 내가 한 것을를 다시 읽고 당신이 계신 이해 이제 ResourceBase

을 제한하는) 자체 참조처럼 :

public abstract class ResourceBase 
{ } 

public abstract class WebResourceBase<T> : ResourceBase 
    where T : WebResourceBase<T> 
{ 
    public T LocalPath { get; set; } 
    public T FtpPath { get; set; } 
} 

public class JavaScript : WebResourceBase<JavaScript> 
{ 
} 

그럼 당신은 JavaScript에서 속성 LocalPathFtpPath는 이제 유형 JavaScript의 것을 볼 수 있습니다.

new JavaScript() 
{ 
    LocalPath = new JavaScript("/path/goes/here/1.js"), 
    FtpPath = new JavaScript("ftp://path/goes/here/1.js") 
} 

는 기본 속성은 현재 유형이어야 이상의 파생, 이하 유도하기 위해 제한됩니다되는이 방법의 장점 :

이제 과제는 JavaScript 유형을 받아 들일 것입니다.


대안 : 명시 구문 분석하는 대신 여기에 제네릭을 사용할 수 없습니다 ResourceBaseLocalPathFtpPath 변수를 떠나, 또는 달리해야하는 경우

, 당신의 암시 운영자가 혼란 스러울 시작합니다 implicit 연산자.

new JavaScript() 
{ 
    LocalPath = JavaScript.Parse("/path/goes/here/1.js"), 
    FtpPath = JavaScript.Parse("ftp://path/goes/here/1.js") 
} 

class JavaScript 
{ 
    public static ResourceBase Parse(string s) 
    { 
     if (path.EndsWith(".js")) 
      return new JavaScript(path); 

     throw new Exception(); 
    } 
} 


대체 : 더 나은는 정적 메서드와 같은 명시적인 것을 제공하는 클래스 계층 구조 대신 implicit 운영자

구워 생성자를 통해 유형에 문자열을 소비 개념의 분석과 특성을 공개 읽기 전용 :

public abstract class ResourceBase 
{ } 

public abstract class WebResourceBase 
{ 
    public ResourceBase LocalPath { get; private set; } 
    public ResourceBase FtpPath { get; private set; } 

    protected abstract ResourceBase ParseLocalPath(string s); 
    protected abstract ResourceBase ParseFtpPath(string s); 
} 

public class JavaScript : WebResourceBase<JavaScript> 
{ 
    protected override ResourceBase ParseLocalPath(string s) 
    { 
     // etc. 
    } 
    protected override ResourceBase ParseFtpPath(string s) 
    { 
     // etc. 
    } 
} 


것은 여보 되려면 est, 이것의 대부분은 문자열에서 특정 유형으로 설정된 두 개의 속성을 얻으려는 약간의 잔인 함으로 보입니다. 많은 옵션이 있습니다. 암시 적 연산자도 작동합니다.

가장 이해하기 쉬운 것을 선택하십시오. 연산자 오버로드는 파기하지 않는 한 다소 숨겨진 경향이 있습니다.

0

난 그냥 프로세스에 간접 지정의 추가 레이어를 추가 할 것입니다. (얼마나 자주 디자인 질문에 대한 좋은 대답인지 놀랍습니다.)).

두 개의 문자열 매개 변수를 사용하거나, 두 개의 추가 문자열 속성을 추가하거나, 두 가지 모두를 사용하는 새 생성자를 추가합니다. (여기에서 두 개의 새로운 문자열 속성을 추가한다고 가정하겠습니다. 거기에서 추론 할 수 있습니다.)

새 속성을 추가하는 경우 : JavaScriptLocalPath 설정 방법은 문자열을에 특정 된 ResourceBase의 파생 형식과 그 결과를 사용하여 LocationPath 속성을 설정합니다. 나는 그것이 메서드를 얻는다고 가정하면 ResourceBase (심지어 string을 저장하는 것을 귀찮게 할 필요가 없도록) 문자열을 추출 할 수 있습니다.

public abstract class WebResourceBase { 
    public ResourceBase LocalPath { get; set; } 
    public ResourceBase FtpPath { get; set; } 

    protected abstract ResourceBase ConvertFromString(string path); 
    public string LocalPathStr { set { LocalPath = ConvertFromString(value); } } 
    public string FtpPathStr { set { FtpPath = ConvertFromString(value); } } 
} 

것은 아마도 향상 될 수 있습니다 :

0

여기에 내 생각이다.

1

내가 너무 ResourceBase

클래스가 자신을 구독 할 수 있습니다 파생 된 기본 클래스에 protected 매핑 메커니즘을 가지고에서 WebResourceBase가 상속하도록되어 있다고 가정합니다

public abstract class ResourceBase 
{ 
    // Records how to make a ResourceBase from a string, 
    // on a per-extension basis 
    private static Dictionary<string, Func<string, ResourceBase>> constructorMap 
     = new Dictionary<string, Func<string, ResourceBase>>(); 

    // Allows a derived type to subscribe itself 
    protected static void Subscribe(
     string extension, 
     Func<string, ResourceBase> ctor) 
    { 
     if (constructorMap.ContainsKey(extension)) 
      throw new Exception("nuh uh"); 

     constructorMap.Add(extension, ctor); 
    } 

    // Given a string, finds out who has signed up to deal with it, 
    // and has them deal with it 
    public static implicit operator ResourceBase(string s) 
    { 
     // Find a matching extension 
     var matches = constructorMap.Where(kvp => s.EndsWith(kvp.Key)).ToList(); 

     switch (matches.Count) 
     { 
      case 0: 
       throw new Exception(
        string.Format("Don't know how to make {0} into a ResourceBase", 
           s)); 
      case 1: 
       return matches.Single().Value(s); 
      default: 
       throw new Exception(string.Format(
        "More than one possibility for making {0} into a ResourceBase", 
        s)); 
     } 
    } 
} 

중간 타입이 대부분입니다 변경되지 않지만 일부 유형 검사를 통해 컴파일 타임에 시행 할 방법을 찾지 못합니다.

public abstract class WebResourceBase : ResourceBase 
{ 
    private ResourceBase localPath; 
    public ResourceBase LocalPath 
    { 
     get { return localPath; } 
     set 
     { 
      if (value.GetType() != GetType()) 
      { 
       throw new Exception("Naughty"); 
      } 
      localPath = value; 
     } 
    }   

    private ResourceBase ftpPath; 
    public ResourceBase FtpPath 
    { 
     get { return ftpPath; } 
     set 
     { 
      if (value.GetType() != GetType()) 
      { 
       throw new Exception("Naughty"); 
      } 
      ftpPath = value; 
     } 
    } 
} 

구체적인 종류는 다음과 같다 :

public class JavaScript : WebResourceBase 
{ 
    public JavaScript() 
    { 

    } 
    private JavaScript(string s) 
    { 
    } 

    static JavaScript() 
    { 
     Subscribe("js", s => (ResourceBase)new JavaScript(s)); 
    } 
} 

사용하면 지정된 것 :

 var js = new JavaScript 
     { 
      LocalPath = "hello.js", 
      FtpPath = "hello.js" 
     }; 

주 그 constructorMap의 서명에 ResourceBaseSubscribe에도 불구하고, 위의 진술 후 LocalPathFtpPathJavaScript 개체입니다.