Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
212 views
in Technique[技术] by (71.8m points)

c# - How to create a new object instance of class being the same type of another instance by only knowing it as base class type?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Downcasting is not possible in C#.

A base object is less than a child object, so you cannot create a base object and cast it to a child.

A base class is missing from the properties and methods added in childs, so you cannot create a base object and convert it to be a child object.

It's like saying that a bicycle is a car, and that it has its properties and capabilities.

But upcasting is OOP fundamental: we can say that child is type of base (a car is just an advanced and extended bicycle).

Here you don't want to up or down cast, but create a new instance as formulated in the suggested new title for the question.

So to create another instance of the real base object type and not a base you can use reflexion and Activator class.

Example to improve in first intention the code provided

public class Base
{
  public string Name;
}

public class Child : Base
{
}

void Go()
{
  var a = new Child { Name = "My Name is A" }; // Legal upcast: Child is type of Base
  var b = (Child)Test(a);
  b.Name = "Sucess";
  Console.WriteLine(a.Name);
  Console.WriteLine(b.Name);
}

Base Test(Base instance)
{
  return (Base)Activator.CreateInstance(instance.GetType());
}

But we can't write:

Base c = new Base();
Child d = (Child)c;  // Illegal downcast: Base is not type of Child

Output

My Name is A
Sucess

The use of a generic will be strongly typed and adapted to solve the problem as exposed

void Go()
{
  var a = new Child { Name = "My Name is A" };
  var b = Test(a);
  Console.WriteLine(a.Name);
  Console.WriteLine(b.Name);
}

T Test<T>(T instance) where T : Base
{
  var instanceNew = Activator.CreateInstance<T>();
  instanceNew.Name = "Sucess";
  return instanceNew;
}

Because using classes, it can be simplified:

T Test<T>(T instance) where T : Base, new()
{
  var instanceNew = new T();
  instanceNew.Name = "Sucess";
  return instanceNew;
}

But be carefull that this generic solution does not work when passing an object as an ancestor class:

var b = Test((Base)a);

Will be processed as Base and not Child.

So we can mix non-generic solution with generic behavior to write:

T Test<T>(T instance) where T : Base, new()
{
  var instanceNew = (T)Activator.CreateInstance(instance.GetType());
  instanceNew.Name = "Sucess";
  return instanceNew;
}

Thus that works better now and it is more consistent:

Base a = new Child { Name = "My Name is A" };
Child b = (Child)Test(a);
Console.WriteLine(a.Name);
Console.WriteLine(b.Name);

Output

My Name is A
Sucess

Possible simplification

Unless you want to do a particular processing in the Test method, on the real type of the parameter, for a new instance by only knowing this parameter as a type of Base, to be able to pass any child, and then return it, to refactor some things for example, you can direclty write:

var a = new Child { Name = "My Name is A" };

var b = (Child)Activator.CreateInstance(a.GetType());

b.Name = "Success";

Note: please, don't use base as a variable name because it is a reserved language keyword, so it does not compile - also, it is an opinion, avoid the use of @base because it is not clean, and in fact it can be source of problems and confusions.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...