2016-10-28 15 views
12

Mam problem z programem C#, który obejmuje następujące elementy:Jak mogę obniżyć instancję generowaną przez metodę statyczną?

class Program 
{ 
    static void Main(string[] args) 
    { 
     Child childInstance = Child.ParseFromA(@"path/to/Afile") as Child; 
    } 
} 

class Parent{ 
    int property; 

    public static Parent ParseFromA(string filename) 
    { 
     Parent parent = new Parent(); 
     // parse file and set property here... 
     return parent; 
    } 
} 

class Child : Parent 
{ 
    public void SomeAdditionalFunction() { } 
} 

Po uruchomieniu tego kodu, childInstance staje null.

Próbowałem poniżej cesją z wyraźną obsady, ale zakończył się wyjątek:
Child childInstance = (Child)Child.ParseFromA(@"path/to/Afile");

Ponieważ chcę analizować niektóre typy plików do Parent i Child przykład, chcę zachować projekt, który generuje instancji przez metody statyczne.

Jak uzyskać prawidłową childInstance?

Odpowiedz

23

Nie można tego spuścić. Gdy obiekt zostanie utworzony jako , będzie on zawsze oznaczony jako . To tak, jakby próbować obniżyć wartość new object() do string: To po prostu nie zadziała - która sekwencja znaków powinna reprezentować ten ciąg znaków?

Zatem jedynym rozwiązaniem jest utworzenie prawidłowego obiektu. Jedyną opcją widzę w twoim przypadku jest, aby metoda statyczna ogólna:

public static T ParseFromA<T>(string filename) where T : Parent, new() 
{ 
    T t = new T(); 
    // parse file and set property here... 
    return t; 
} 

Zastosowanie:

Child childInstance = Parent.ParseFromA<Child>(@"path/to/Afile"); 

Ogólny ograniczenie T : Parent zapewnia T jest podtypem Parent i new() zapewnia, że ​​T ma konstruktor bez parametrów.

+2

Dzięki! Twoje rozwiązanie jest dokładnie tym, co chciałem zrobić! Zastosowałem to i doskonale działało. – Malboma99

+2

To tylko mała rzecz na temat twojego przykładu: całkowicie normalne jest spychanie "Zwierzęcia" do "Kota": otrzymujesz "Kot", jeśli był to "Kot" i "null", jeśli nie był (to jest co autor robi). Problem polega na tym, że jeśli utworzysz "Zwierzę", stworzysz abstrakcyjne zwierzę, które nie może być kotem. Jest to model, który po prostu nie ma swojego rodzaju. Jeśli mówisz o mężczyźnie, nie masz na myśli "John", masz na myśli jakiegoś abstrakcyjnego człowieka. Jestem prawie pewien, że to była intencja, ale myślałem, że jest to trochę niejasne z twojego przykładu. – Archeg

+1

@Archeg: Masz oczywiście całkowitą rację. Zmodyfikowalem moj przyklad i zdecydowalem sie na niektóre klasy szkieletowe. – Heinzi

3

Jeśli upierasz się przy użyciu metody statyczne i nie chcą używać refleksji lub rodzajowych, a następnie można również rozważyć użycie new słowa kluczowego:

class Parent 
{ 
    public static Parent ParseFromA(string filename) 
    { 
     Parent parent = new Parent(); 
     parent.Parse(filename); 
     return parent; 
    } 

    protected virtual void Parse(string fileName) 
    { 
     ... 
    } 
} 

class Child : Parent 
{ 
    public new static Child ParseFromA(string filename) 
    { 
     Child child = new Child(); 
     child.Parse(filename); 
     return parent; 
    } 

    protected override void Parse(string fileName) 
    { 
     base.Parse(fileName); 
     SomeAdditionalFunction(); 
    } 
} 

Osobiście po prostu użyć metody instancji.

var child = new Child(...); 
child.Parse(...); 

Dodatkowa linia kodu to niewielka cena za czystszy kod, IMHO. static słowo kluczowe nie działa dobrze z dziedziczeniem, jak widać. Można też zawsze zawinąć metodę instancji na metodę rozszerzenia, jeśli chcesz jedną wkładkę po wszystkim:

public static class ParentEx 
{ 
    public static T ParseFile<T>(this T source, string fileName) : where T : Parent 
    { 
     source.Parse(fileName); 
     return source; 
    } 
} 

a następnie

var child = new Child().ParseFile(fileName); 
+0

Lub użyj wirtualnego w klasie bazowej, przesłonięcie klasy potomnej – Mafii

+0

@Mafii na przykładowe metody, tak. Nie można jednak zastąpić metod statycznych. –

2

Jeśli metoda statyczna nie wie, jakiego rodzaju do tworzenia, ty trzeba go przekazać. Na przykład przy użyciu generycznych:

namespace ConsoleApplication18 
{ 
    class Program 
    { 
    static void Main(string[] args) 
    { 
     var childInstance = Parent.ParseAs<Child>(@"path/to/Afile"); 

     childInstance.SomeAdditionalFunction(); 
    } 
    } 

    class Parent 
    { 
    int property; 

    public static T ParseAs<T>(string filename) where T : Parent, new() 
    { 
     var parent = new T(); 

     // parse file and set property here... 
     parent.property = 42; 

     return parent; 
    } 
    } 

    class Child : Parent 
    { 
    public void SomeAdditionalFunction() { } 
    } 
} 
1

Możesz rzucić tylko do klasy rodziców, a nie do klasy dzieci. Kompilator nie mógł bezpiecznie założyć, że obiekt został poprawnie skonstruowany, ma wszystkie niezbędne właściwości, aby mógł być bezpiecznie dostępny jako obiekt podrzędny.

Używaj ogólnej metody, jak wspomniano powyżej, przez Heinzi lub użyj sparametryzowanego konstruktora i instancyjnej metody analizy w klasie rodzica i dziecka.

class Parent 
{ 
    public Parent() { } 
    public Parent(string fileName) 
    { 
     Parse(fileName); 
    } 

    private void Parse(string fileName) 
    { 
     // Do your parsing stuff here. 
    } 
} 

class Child : Parent 
{ 
    public Child() { } 
    public Child(string fileName) : base(fileName) 
    { 
     // Parsing is done already done within the constructor of Parent, which is called by base(fileName) 
     // All you need to do here is initialize the rest of your child object. 
    } 
} 
Powiązane problemy