Страницы

Поиск по вопросам

понедельник, 1 октября 2018 г.

Какой потомок вызвал статический метод родителя? или миссия невыполнима?

Я получил тестовое задание во время собеседования на должность 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",

Комментариев нет:

Отправить комментарий