Класс-метод в стиле Python для C #? - PullRequest
5 голосов
/ 13 января 2010

Есть ли способ сделать то, что classmethod делает в Python в C #?

То есть статическая функция, которая получает объект Type в качестве (неявного) параметра в зависимости от того, из какого подкласса он используется.

Пример того, что я хочу, примерно, это

class Base:
    @classmethod
    def get(cls, id):
        print "Would instantiate a new %r with ID %d."%(cls, id)

class Puppy(Base):
    pass

class Kitten(Base):
    pass

p = Puppy.get(1)
k = Kitten.get(1)

ожидаемый результат

Would instantiate a new <class __main__.Puppy at 0x403533ec> with ID 1.
Would instantiate a new <class __main__.Kitten at 0x4035341c> with ID 1.

(здесь тот же код на кодовой панели.)

Ответы [ 5 ]

3 голосов
/ 13 января 2010

Я думаю, вы хотите взглянуть на дженерики.

Руководство от MSFT:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

2 голосов
/ 14 января 2010

В зависимости от контекста вам нужны универсальные или виртуальные методы.

1 голос
/ 15 января 2010

В принципе, вы могли бы написать что-то вроде этого:

class Base
{
    public static T Get<T>(int id)
        where T : Base, new()
    {
        return new T() { ID = id };
    }

    public int ID { get; set; }
}

Тогда вы могли бы написать var p = Base.Get<Puppy>(10). Или, если вы чувствуете себя мазохистом, вы можете написать Puppy.Get<Puppy>(10) или даже Kitty.Get<Puppy>;) Во всех случаях вы должны передавать тип явно, а не неявно.

Кроме того, это тоже работает:

class Base<T> where T : Base<T>, new()
{
    public static T Get(int id)
    {
        return new T() { ID = id };
    }

    public int ID { get; set; }
}

class Puppy : Base<Puppy>
{
}

class Kitten : Base<Kitten>
{
}

Вам все еще нужно передать тип обратно в базовый класс, что позволит вам написать Puppy.Get(10) как положено.

Но все же, есть ли причина писать это так, когда var p = new Puppy(10) столь же лаконичен и более идиоматичен? Вероятно, нет.

0 голосов
/ 26 июня 2014

Поскольку C # статически компилируется, вызов Puppy.get() разрешается во время компиляции, например. известно, что он указывает на Base.get(), и это означает, что сгенерированный CIL (Common Intermediate Language) будет иметь инструкцию вызова для Base.get():

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  call       void Base::get(int32)
  IL_0007:  nop
  IL_0008:  ret
} 
0 голосов
/ 15 января 2010

Это эквивалентно методам класса в Object Pascal. (реализация .Net будет оксигеном RemObject).

Однако, хотя ссылки на классы и, следовательно, методы виртуальных классов или, казалось бы, статические методы, которые могут получить доступ к некоторому состоянию уровня класса, хороши в оригинальной платформе, я не думаю, что они имеют смысл для .Net или C #. *

Раньше я также программировал на Oxygene в течение нескольких лет, и мне никогда не требовались ссылки на классы.

Не потому, что я не знал, как их использовать. В «оригинальной» платформе ObjectPascal, родной Delphi, я использовал их все времени. Но потому что .Net и BCL не имеют реальной поддержки для них, поэтому у вас нет никаких преимуществ при их использовании. В то время как платформы, такие как python или нативный Delphi, поддерживают их в своих библиотеках.

Очевидно, что вам не нужен статический метод, вам нужно то, что может иметь состояние и / или поддерживать наследование. Так что либо фабрики, либо в вашем случае конструктор тоже подойдет.

...