Как я могу скрыть экземпляр, созданный статическим методом?
У меня проблема с программой С#, которая включает в себя следующее:
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() { }
}
При запуске этого кода childInstance
становится null
.
Я попытался выполнить следующее задание с явным приложением, но закончил с исключением:
Child childInstance = (Child)Child.ParseFromA(@"path/to/Afile");
Так как я хочу разбирать некоторые типы файлов в экземплярах Parent
и Child
, я хочу сохранить конструкцию, которая генерирует экземпляры статическими методами.
Как мне получить правильный childInstance
?
Ответы
Ответ 1
Вы не можете его понизить. Когда объект был создан как Parent
, он всегда будет Parent
. Это похоже на попытку спускать a new object()
в string
: это просто не сработает - какая последовательность символов должна представлять эта строка?
Таким образом, ваше единственное решение - создать правильный объект. Единственный вариант, который я вижу в вашем случае, - сделать ваш статический метод общим:
public static T ParseFromA<T>(string filename) where T : Parent, new()
{
T t = new T();
// parse file and set property here...
return t;
}
Использование:
Child childInstance = Parent.ParseFromA<Child>(@"path/to/Afile");
Общее ограничение T : Parent
гарантирует, что T
является подтипом Parent
, а new()
гарантирует, что T
имеет конструктор без параметров.
Ответ 2
Если вы настаиваете на использовании статических методов и не хотите использовать отражение или дженерики, то вы также можете использовать ключевое слово new
:
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();
}
}
Лично я бы просто использовал методы экземпляра.
var child = new Child(...);
child.Parse(...);
Дополнительная строка кода - небольшая цена для оплаты более чистого кода, ИМХО. static
ключевое слово не очень хорошо работает с наследованием, как вы можете видеть. Вы также можете всегда обернуть метод экземпляра в метод расширения, если вы хотите, чтобы один-лайнер в конце концов:
public static class ParentEx
{
public static T ParseFile<T>(this T source, string fileName) : where T : Parent
{
source.Parse(fileName);
return source;
}
}
а затем
var child = new Child().ParseFile(fileName);
Ответ 3
Если ваш статический метод не знает, какой тип для создания, вам нужно его передать. Например, используя дженерики:
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() { }
}
}
Ответ 4
Вы можете использовать только родительский класс, а не дочерний класс. Компилятор не мог смело предположить, что объект был построен правильно, имеет все необходимые свойства, которые должны безопасно получать в качестве дочернего объекта.
Использовать общий метод, упомянутый выше Heinzi, или использовать параметризованный конструктор и метод instanced parsing в родительском и дочернем классе.
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.
}
}