Какой потомок вызвал статический метод родителя? или миссия невыполнима?
1,00
р.
р.
Я получил тестовое задание во время собеседования на должность C# программиста, но так и не смог его решить, т.к. не понял что именно требуется. Пишу это, т.к. хочу совершенствоваться, заканчивать все начатые дела, понимать и знать то что до этого не понимал или не знал по мере возможности. Со словами "вот лёгенькое тестовое задание" интервьюер отправил мне email. Цитирую постановку: Классы А и В не включают реализации свойств, полей и методов, не атрибутированы и являются наследниками общего предка С 1) не создавая экземпляров описанных классов, реализовать в классе C статический метод с сигнатурой public static string GetName(), для которого истинно A.GetName() == "A" && B.GetName() == "B" 2) реализовать единый счетчик количества экземпляров всех наследников класса С с сигнатурой поля public static int Count в классе, не лежащем вне линии наследования Я уточнил у интервьюера, что имеется в виду под константами "A" и "B" - он ответил, что это именно имена классов. Т.е. метод GetName() должен возвращать имя класса потомка. В процессе тщетных попыток реализовать в точности как описано в задании и яростного гугления был найден материал со stackoverflow: How to get the class Type in a base class static method in .NET? Get inherited caller type name in base static class Где, на сколько я понял, утверждается, что получить имя класса потомка из статического метода невозможно без хаков. Я прикидывал ещё несколько вариантов (например, создание нового класса, который не описан в задании), но все они связаны с попытками обхода условий, что не вяжется со словами интервьюера о том, что есть конкретное решение, думаю оно должно быть честное и простое, раз он так вначале утверждал. У меня получилось сделать только это, но я знаю, что это неверно, т.к. метод GetName не статический и создаются экземпляры класса: 1. using System namespace Rextester { public class Program { public static void Main(string[] args) { Console.WriteLine ( new A().GetName() == "A" && new B().GetName() == "B" ) } } class C { public string GetName() { return this.GetType().Name } } class A : C{} class B : C{} } 2. using System namespace Rextester { public class Program { public static void Main(string[] args) { var a = new A() var b = new B() var c = new C() } } public class C { public static int Count public C () { if ( this is A || this is B ) Count++ Console.WriteLine ( "Count is: " + Count ) } } public class A : C{} public class B : C{} } Ответ интервьювера: А:добрый день, я вижу, однако вынужден огорчить, задача имеет конкретное решение Б:Т.е. ни один из двух пунктов неверный? А:второй зависит от первого А:и соотвественно, первый не верен, так как создавать экземпляров нельзя Пожалуйста, помогите мне решить эту задачу. Мне кажется, что в самой постановке есть противоречия, тем более в ней используется двойное отрицание, что трудно для понимания. Обновлено: Коллеги, благодаря вашим ответам, пояснениям и замечаниям мне всё-таки удалось решить задачу так: создал ещё один класс C, который сделал базовым для всех и установил в нём счётчик. Новый класс обеспечил выполнение условия "А и B являются наследниками общего предка С". using System namespace Rextester { public class Program { public static void Main ( string[] args ) { Console.WriteLine ( A.GetName() == "A" && B.GetName() == "B" ) new A () // 1 new B () // 2 new C () // 3 new A () // 4 new B () // 5 } } class C { public static int Count } class C < T > : C where T: C < T > { public C () { if ( this is A || this is B ) Count++ Console.WriteLine ( "Count is:" + Count ) } public static string GetName(){ return typeof ( T ).Name } } class A : C < A > { } class B : C < B > { } } Благодаря ответам и замечаниям иностранных коллег, этот код я дополнил соответствующими модификаторами: базовый класс C и C < T > пометил модификатором abstract, определяющим данные классы как базовые, а поле Count дополнил геттером и сеттером ( с модификатором доступа protected ), для ограничения доступа к переменной. Так же конструктор базового класса C помечен модификатором доступа protected. Допиленный вариант решения: using System namespace Rextester { public class Program { public static void Main ( string[] args ) { Console.WriteLine ( A.GetName() == "A" && B.GetName() == "B" ) new A () new B () } } abstract class C { public static int Count { get protected set } } abstract class C < T > : C where T: C < T > { protected C () { if ( this is A || this is B ) Count++ Console.WriteLine ( "Count is:" + Count ) } public static string GetName(){ return typeof ( T ).Name } } class A : C < A > { } class B : C < B > { } } Спасибо всем, кто участвовал в решении этой головоломки! Вы потрясающие люди)
Ответ Как сказано в ответах по ссылке. В простейшем случае class C { public static string GetName() => ... } class A : C {} class B : C {} В методе GetName нельзя узнать тип наследника, просто потому, что статические методы не переопределяются, и в IL будет стоять не call A.GetName а call C.GetName
Так как ограничений на класс C нет, его можно сделать generic классом. Наследники будут специфицировать generic параметр собой и в этом случае базовый класс может выглядеть так: class C { public static string GetName() => typeof(T).Name } Наследники: class A : C { } class B : C { } В этом случае: A.GetName() вернет "A", B.GetName() вернет "B",