2010-01-15 3 views
5

У меня есть некоторые методы расширения, которые могут быть использованы, как это:C методы # расширения на «членов»

MyType myObject; 
string displayName = myObject.GetDisplayName(x => x.Property); 

Проблема здесь состоит в том, что она нуждается экземпляр, даже если метод расширения необходим только тип MyType. Поэтому, если нет экземпляра, его следует называть следующим образом:

string displayName = BlahBlahUtility.GetDisplayName((MyTpe x) => x.Property); 

Что не так приятно больше.

Есть ли способ написать лучший синтаксис для таких случаев?

То, что я на самом деле хочу, чтобы сделать это (псевдо язык):

string displayName = MyType.Property.GetDisplayName() 

Что, конечно, не работает с C#.

Но что-то вроде этого:

string displayName = ((MyType x) => x.Property).GetDisplayName(); 

Это тоже не возможно (после того, как лямбда, точка не принимается).

Любые идеи?


Edit:

Мой "любимый синтаксис" MyType.Property.GetDisplayName(), кажется, вводит в заблуждение. Здесь я не говорю о статических свойствах. Я знаю, что этот синтаксис не будет возможен. Я просто пытался показать на псевдоязыке, какая информация необходима. Это было бы идеально, каждый дополнительный материал просто синтаксический накладные расходы. Любой рабочий синтаксис, близкий к этому, будет отличным.

Я не хочу писать определенный метод расширения. Я хочу простой, понятный и компилируемый тайм-код, с использованием любой языковой функции.

+1

Что именно вы пытаетесь достичь? –

+0

Это статическое отражение над именами участников в типе – saret

+1

Вы не можете создавать статические методы расширения в C# 3. Если это ваш собственный тип, который вы расширяете, почему вы не можете добавить этот метод напрямую? –

ответ

0

Похоже, вы пытаетесь создать статический метод расширения?

DateTime yesterday = DateTime.Yesterday(); // Static extension. 

Вместо

DateTime yesterday = DateTime.Now.Yesterday(); // Extension on DateTime instance. 

Если это то, что вы пытаетесь снять, я не считаю, что это возможно в текущей версии C#.

+0

Мне не нужен статический метод расширения. Мне нужен простой синтаксис, используя любую функцию языка. Я добавил раздел на вопрос, чтобы объяснить его лучше. –

1

От вашего комментария: «Я хочу простой и скомпилировать безопасный синтаксис для получения информации о членах».

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

Этот блог объясняет, почему:

http://blogs.msdn.com/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx

Так что сейчас, вы только собираетесь бороться против отсутствующей функции.Возможно, вы могли бы разместить дополнительную информацию о своей более широкой проблеме и посмотреть, могут ли люди предлагать разные подходы.

Update

Без дополнительной информации о вашей проблеме это только догадки. Но если у вас есть свойство, которое представляет значение, но также несет дополнительную «мета» информацию, вы всегда можете представить это как новый тип и использовать шаг «инъекции», чтобы все установить.

Вот предложили абстрактный интерфейс к такой «мете собственности»:

public interface IMetaProperty<TValue> 
{ 
    TValue Value { get; set; } 

    string DisplayName { get; } 

    event Action<TValue, TValue> ValueChanged; 
} 

Значения свойства просто еще один суб-свойство, с его типом, определенным пользователем.

Я включил отображаемое имя, а также в качестве бонуса у вас есть событие, которое срабатывает при изменении значения (так что вы получаете «наблюдаемость» бесплатно).

Чтобы иметь такие свойства, как это в классе, вы бы объявить его как это:

public class SomeClass 
{ 
    public IMetaProperty<string> FirstName { get; private set; } 
    public IMetaProperty<string> LastName { get; private set; } 
    public IMetaProperty<int> Age { get; private set; } 

    public SomeClass() { MetaProperty.Inject(this); } 
} 

Обратите внимание, как сеттеры по свойствам являются частными. Это останавливает любого пользователя от случайной установки самого свойства вместо установки подэлемента Value.

Так что это означает, что класс должен установить эти свойства, чтобы они были не только null. Он делает это, вызывая магический Inject метод, который может работать на любом классе:

public static class MetaProperty 
{ 
    // Make it convenient for us to fill in the meta information 
    private interface IMetaPropertyInit 
    { 
     string DisplayName { get; set; } 
    } 

    // Implementation of a meta-property 
    private class MetaPropertyImpl<TValue> : IMetaProperty<TValue>, 
              IMetaPropertyInit 
    { 
     private TValue _value; 

     public TValue Value 
     { 
      get { return _value; } 
      set 
      { 
       var old = _value; 
       _value = value; 
       ValueChanged(old, _value); 
      } 
     } 

     public string DisplayName { get; set; } 

     public event Action<TValue, TValue> ValueChanged = delegate { }; 
    } 

    public static void Inject(object target) 
    { 
     // for each meta property... 
     foreach (var property in target.GetType().GetProperties() 
      .Where(p => p.PropertyType.IsGenericType && 
         p.PropertyType.GetGenericTypeDefinition() 
          == typeof(IMetaProperty<>))) 
     { 
      // construct an implementation with the correct type 
      var impl = (IMetaPropertyInit) 
       typeof (MetaPropertyImpl<>).MakeGenericType(
        property.PropertyType.GetGenericArguments() 
       ).GetConstructor(Type.EmptyTypes).Invoke(null); 

      // initialize any meta info (could examine attributes...) 
      impl.DisplayName = property.Name; 

      // set the value 
      property.SetValue(target, impl, null); 
     } 
    } 
} 

Он просто использует отражение, чтобы найти все IMetaProperty слотов, скрывающихся в объекте, и заполняет их с реализацией.

Так что теперь пользователь SomeClass мог сказать:

var sc = new SomeClass 
      { 
       FirstName = { Value = "Homer" }, 
       LastName = { Value = "Simpson" }, 
       Age = { Value = 38 }, 
      }; 

Console.WriteLine(sc.FirstName.DisplayName + " = " + sc.FirstName.Value); 

sc.Age.ValueChanged += (from, to) => 
    Console.WriteLine("Age changed from " + from + " to " + to); 

sc.Age.Value = 39; 

// sc.Age = null; compiler would stop this 

Если вы уже используете контейнер МОК, вы можете быть в состоянии достичь некоторых из этого не заходя непосредственно к размышлению.

+0

Разве нет лучшей идеи, а затем вызов статического метода обычно? –

+0

Возможно, если вы разместите дополнительную информацию о своей более широкой проблеме. например в зависимости от масштаба того, что вы делаете, генерация кода может помочь, или это может быть излишним. –

+0

Обновлено с другой идеей. –

0

Звучит так, будто вы слишком сильно интегрируете слои. Обычно в такой ситуации я бы позволил слою представления решить реализацию GetDisplayName() вместо того, чтобы сделать его расширением самого свойства. Вы могли бы создать интерфейс под названием MyTypeDisplayer или что бы вы ни предложили, и пусть будет множество его реализаций, не ограничивая вас одной реализацией отображения.

+0

Нет, если 'GetDisplayName' имеет объект, являющийся значением MyType.Property, тогда у него нет PropertyInfo для этого свойства. Он имеет только значение свойства. Например, он не может получить пользовательские атрибуты. –

+0

Это не имеет ничего общего со слоями. DisplayName - всего лишь пример. Например, он может возвращать данные, взятые из атрибутов. –

+0

@Earwicker, О, это имеет смысл, я отредактирую. –

0

Проблема в том, что нельзя получить ссылку на нестатические методы с помощью экземпляра MyType. [Member]. Их можно увидеть только через ссылку на экземпляр типа. Вы также не можете создать метод расширения сверху объявления типа, только в экземпляре типа - то есть сам метод расширения должен быть определен с использованием экземпляра типа (это T x).

Один однако можно определить выражение, как это, чтобы получить ссылку на статические члены: ((MyType х) => MyType.Property)

Можно сделать что-то похожее на струнной DISPLAYNAME = ((MyType х) => x.Property) .GetDisplayName(); Первая проблема заключается в том, что компилятор рассматривает ваш (x => x.Property) как выражение, а не действие/func и т. Д. Для этого можно было бы нужно сделать это:

string displayName = ((Expression<Func<PropertyType>>)((MyType x) => x.Property).GetDisplayName(); 

Метод расширения затем должны быть определены следующим образом:

public static string GetDisplayName<T>(this Expression<Func<T>> expression) 

Вы также могли бы определить метод расширения на вершине Expression<Action>> and Expression<Action<T>> если ваши члены также являются методами.

Вы можете сделать точку после выражения - здесь будет существовать метод компиляции.

Добавив:

Я думаю, что статический вызов метода расширения в случаях, когда один не имеет экземпляр типа один должен сделать «отражение» на определение имени членов будет самым чистым синтаксисом все еще - таким образом вы все равно можете использовать метод расширения при использовании экземпляра типа и вернуться к определению статического вызова =>MyExtensionClass.GetDisplayName(TypeOfX x => TypeOfX.StaticMember OR x.Property/Member), если у вас нет экземпляра

+0

Да, я уже пробовал бросать лямбду в выражение. Но это синтаксически далека от идеала, поэтому я даже не упоминал об этом. Тогда еще лучше вызвать статический метод напрямую. –

+0

Я согласен - также обнаружил, что нужно указать явное выражение в выражении (не могу использовать выражение var = (t => t.Property); либо как компилятор, вероятно, не увидит его как выражение, а просто Func < PropertyType> определение - так что вам понадобится var, чтобы стать выражением >) Unfortuanetly Я не уверен в том, как получить синтаксис в чистое состояние, которое вам бы хотелось - также прошел вызов статического метода расширения маршрут, поскольку он казался чистым ... MyExtensionClass.GetDisplayName (x => TypeOfX.StaticMember), но может использовать метод расширения, если у вас есть экземпляр типа – saret

+0

Извините, что я имел в виду - я согласен - также найдено, что нужно поставить явное выражение выражение в выглядит грязный синтаксис wise – saret

0

Если вы связываете свои объекты, вы можете сделать расширение на интерфейсе:

namespace Linq1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyType o = new MyType(); 
      o.Property.GetDisplayName(); 
     } 
    } 

    public class MyType 
    { 
     public IDisplayableProperty Property { get; set; } 
    } 

    public interface IDisplayableProperty 
    { 
     string GetText(); 
    } 

    public class MyProperty1 : IDisplayableProperty 
    { 
     public string GetText() { return "MyProperty2"; } 
    } 

    public class MyProperty2 : IDisplayableProperty 
    { 
     public string GetText() { return "MyProperty2"; } 
    } 

    public static class Extensions 
    { 
     public static string GetDisplayName(this IDisplayableProperty o) 
     { 
      return o.GetText(); 
     } 
    } 
} 
+0

Возможно, мне что-то не хватает, но почему бы просто не переименовать метод GetText' в 'GetDisplayName' и забыть о методе расширения? –

+0

Hm, qutie справа. Полагаю, я это сделал, думая о статической точке входа «Extensions.GetDisplayName (o.Property)» –

+0

Я не могу и не хочу влиять на MyType. Это может быть любой тип. Иначе это было бы легко. –

Смежные вопросы