Есть ли способ сделать методы классов в C # как Python? - PullRequest
2 голосов
/ 19 августа 2011

В python метод экземпляра self указывает на экземпляр класса, так же, как this в C #.В python метод класса self указывает на класс.Есть ли эквивалент C #?

Это может быть полезно, например:

Пример Python:

class A:
  values = [1,2]

  @classmethod
  def Foo(self):
    print "Foo called in class: ", self, self.values

  @staticmethod
  def Bar():
    print "Same for all classes - there is no self"

class B(A):
  # other code specific to class B
  values = [1,2,3]
  pass

class C(A):
  # other code specific to class C
  values = [1,2,3,4,5]
  pass

A.Foo()
A.Bar()
B.Foo()
B.Bar()
C.Foo()
C.Bar()

Результаты в:

Foo called in class:  __main__.A [1, 2]
Same for all classes - there is no self
Foo called in class:  __main__.B [1, 2, 3]
Same for all classes - there is no self
Foo called in class:  __main__.C [1, 2, 3, 4, 5]
Same for all classes - there is no self

Этоможет быть отличным инструментом, так что общий код в контексте класса (без экземпляра) может обеспечить настраиваемое поведение, которое определяется подклассом (без требования экземпляра подкласса).

Мне кажется, что C #статические методы в точности похожи на статические методы питонов, в том смысле, что нет доступа к тому, какой класс фактически использовался для вызова метода.

Но есть ли способ сделать методы класса в C # ??Или хотя бы определить, какой класс вызвал метод, например:

public class A
{
  public static List<int> values;

  public static Foo()
  {
    Console.WriteLine("How can I figure out which class called this method?");
  }
}

public class B : A
{
}

public class C : A
{
}

public class Program
{
  public static void Main()
  {
    A.Foo();
    B.Foo();
    C.Foo();
  }
}

Ответы [ 4 ]

2 голосов
/ 19 августа 2011

Нет способа сделать это, используя обычные статические методы. Возможные альтернативы включают в себя:

1) Виртуальные, переопределенные методы экземпляра:

public class A
{
    public virtual void Foo()
    {
        Console.WriteLine("Called from A");
    }
}

public class B : A
{
    public override void Foo()
    {
        Console.WriteLine("Called from B");
    }
}

2) Методы расширения:

public class A
{
}

public class B : A
{
}

public static class Extensions
{
    /// Allows you to do:
    /// var whoop = new B();
    /// whoop.Foo();
    public static void Foo<T>(this T thing) where T : A
    {
        Console.WriteLine("Called from " + thing.GetType().Name);
    }
}

3) Предполагая, что A и B имеют конструктор по умолчанию:

public static class Cached<T> where T : class, new()
{
    private static T _cachedInstance;

    public static T Instance
    {
        get { return _cachedInstance ?? (_cachedInstance = new T()); }
    }
}

public static class Extensions
{
    public static void Example()
    {
        Cached<B>.Instance.Foo();
    }

    public static void Foo<T>(this T thing) where T : A, new()
    {
        Console.WriteLine("Called from " + typeof(T).Name);
    }
}
1 голос
/ 19 августа 2011

Не как таковой.Каждый вызывающий метод должен помещать себя и переменную this в некоторый статически доступный стек или словарь.

Вы можете использовать CallContext для хранения стека вызовов.Однажды я использовал такой механизм для хранения информации на основе стека в цепочке вызовов функций.

Вы можете использовать инфраструктуру AOP, такую ​​как Postsharp , для обработки CallContext.Это то, что я сделал.Я использовал это именно для этой цели.Я встраивал IronPython в свое приложение и хотел найти способ идентифицировать объект и метод C #, которые инициировали вызов в IronPython.Это сработало довольно хорошо.К сожалению, у меня больше нет этого кода.

0 голосов
/ 19 августа 2011

Для этого вы можете использовать обобщения

public class _A<T> where T : _A<T> {

  public static int[] Values=new int[] {1,2};

  public static void Foo() {
    Console.WriteLine(String.Format("Foo called in class: {0} {1}",typeof(T).Name, String.Join(",",T.Values)));
  }
}

public class A : _A<A> {
}

public class B : _A<B> {
  public static new int[] Values=new int[] {1,2,3};
}

public class C : _A<C> {
  public static new int[] Values=new int[] {1,2,3,4,5};
}

Сложность в том, что вам нужно знать переменную Type, чтобы использовать A, чтобы вы не могли выполнить A.Foo (), но вы могли бы сделать B.Foo () и C.Foo ().Вы могли бы однако A.Foo () и получить

0 голосов
/ 19 августа 2011

Вот соответствующий код на C #:

public class A {

  protected int[] values;

  public A () {
    values = new int[] { 1, 2 };
  }

  public void Foo() {
    Console.WriteLine("Foo called in class: {0}, values = {1}", this.GetType().Name, String.Join(",", values));
  }

  public static void Bar() {
    Console.WriteLine("Same for all classes.");
  }

}

public class B : A {

  public B () {
    values = new int[] { 1, 2, 3 };
  }

}

public class C : A {

  public C () {
    values = new int[] { 1, 2, 3, 4, 5 };
  }

}

Для вызова методов экземпляра необходимо создать экземпляры классов:

new A().Foo();
A.Bar();
new B().Foo();
B.Bar();
new C().Foo();
C.Bar();

Вывод:

Foo called in class: A, values = 1,2
Same for all classes.
Foo called in class: B, values = 1,2,3
Same for all classes.
Foo called in class: C, values = 1,2,3,4,5
Same for all classes.

Вызов C.Bar() эквивалентен вызову A.Bar(), метод не будет знать о разнице.Метод находится в классе A, но компилятор позволяет вызывать его также с использованием производных классов.

...