Я получил тестовое задание во время собеседования на должность 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
Наследники:
class A : C { }
class B : C { }
В этом случае: A.GetName() вернет "A", B.GetName() вернет "B",
Комментариев нет:
Отправить комментарий