"Slicing" here refers to the C use of that term. For reference: What is object slicing?
I thought about this in the following context:
I have this Person:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
public virtual void Greet()
{
Console.WriteLine("Hello!");
}
}
}
Teacher
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Teacher : Person
{
public Teacher() : base("empty")
{
}
public override void Greet()
{
base.Greet();
Console.WriteLine("I'm a teacher");
}
}
}
Student
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Student : Person
{
public Student() : base("empty")
{
}
public override void Greet()
{
base.Greet();
Console.WriteLine("I'm a student!");
}
}
}
and Main
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
Student s = new Student();
Teacher t = new Teacher();
List<Person> persoane = new List<Person>();
persoane.Add(s);
persoane.Add(t);
foreach(Person person in persoane)
{
person.Greet();
}
}
}
}
I expected to see "Hello!" twice on the screen (because of this slicing concept) but I got Hello! I'm a student and Hello! I'm a teacher.
According to MS Docs, this is an implicit cast. But in C , due to object slicing, I'm very much sure that I would've got "Hello!" twice.
So, my questions are: Does C# have slicing? Also, if I wanted to use Student and Teacher as a Person, how would I do that? (without changing the classes, only Main)
Thanks!
CodePudding user response:
You can achieve a similar effect by applying the new
keyword instead of override
on the method, but it's not slicing the object as in C :
Person p = new Person();
p.Greet();
Student s = new Student();
s.Greet();
Person ps = s;
ps.Greet();
Student s2 = (Student)ps;
s2.Greet();
class Person
{
public virtual void Greet()
{
Console.WriteLine("Hello!");
}
}
class Student : Person
{
new public void Greet() // note new here
{
Console.WriteLine("I'm a student!");
}
}
Output:
Hello! // from Person
I'm a student! // from Student
Hello! // from Student assigned to Person
I'm a student! // from Student as Person casted back to Student
How does it work?
First I emphasized that we use the new
modifier to hide a method explicitly. If you don'g use that, it'll generate a compiler warning. Next, member lookup is strictly defined in C# and considers hidden methods.
As a result we see in IL code is that the compiler generates different methods calls:
IL_0008: callvirt instance void xxx.Person::Greet()
...
IL_0015: callvirt instance void xxx.Student::Greet()
...
IL_001e: callvirt instance void xxx.Person::Greet()
...
IL_002c: callvirt instance void xxx.Student::Greet()
but in the debugger we see that there are 4 local variable but only two objects:
0:000> !clrstack -a
...
010ff1b8 017f0929 xxx.Program.Main(System.String[]) [C:\...\Program.cs @ 22]
PARAMETERS:
args (0x010ff1d4) = 0x031a2420
LOCALS:
0x010ff1d0 = 0x031a244c // p
0x010ff1cc = 0x031a41a8 // s
0x010ff1c8 = 0x031a41a8 // ps
0x010ff1c4 = 0x031a41a8 // s2
Why is that? Each object in C# carries along its type information. That thing is called method table (MT), basically a number of pointer size which is unique for types. You can't slice that number into meaningful smaller parts.
I have not understood all the details about the method table and related stuff yet.