Strongly typed property reference to several classes without a common interface (C #)

The namespace System.Windows.Documentsincludes several classes with a Inlinestype property InlineCollection. For example, classes Paragraph, Boldand Hyperlinkhave this property.

Each of these classes is decorated ContentPropertyAttribute...

[ContentPropertyAttribute("Inlines")]
public class Paragraph : Block

... this means that it is easy enough, using reflection, to find that a given object provides this property.

However, I need to have access to this property in a strongly typed way by choosing the types that implement it.

I am a little surprised that Microsoft did not force all of these classes to implement the " IInlineContainer" interface , which would simplify type checking and casting.

However, in the absence of such an interface, is there any way to fake this polymorphic functionality, ideally, without clogging my code with a lot of conditions and type checking?

Thanks so much for your ideas,

Tim

Edit:

Thanks for your suggestions. A number of people have proposed the idea of ​​a wrapper class, but this is not possible in my situation, since the targets were not created by my code, but by other classes in the .NET environment, for example, the Xaml parser or the RichTextBox control (in which the containing one is edited FlowDocument).

Edit 2:

There were some great suggestions here, and I thank everyone who shared their ideas. The solution I chose to implement uses the extension methods that were proposed by @qstarin, although I refined the concept to suit my needs as follows:

public static InlineCollection GetInlines(
    this FrameworkContentElement element)
{
    if (element == null) throw new ArgumentNullException("element");

    if (element is Paragraph)
    {
        return ((Paragraph) element).Inlines;
    }
    else if (element is Span) // also catches Bold, Italic, Unerline, Hyperlink
    {
        return ((Span)element).Inlines;
    }
    else 
    {
        return null;
    }
}

( , ), , , .

+3
8

.

public static class InlineContainerExtensions {
    public static InlineContainer GetInlines(this Paragraph inlineContainer) {
        return inlineContainer.Inlines;
    }

    public static InlineContainer GetInlines(this Bold inlineContainer) {
        return inlineContainer.Inlines;
    }
}
+2

, dynamic:

dynamic doc = new Bold()
doc.InlineCollection. ...
doc = new Paragraph()
doc.InlineCollection. ...

, , Bold, Paragraph ..

+1

-, Inlines .

, , Inlines

+1

, , , , .

, , , , ..:

[InlineContainerAdapter(typeof(SpecificClass1))]
public class WrapSpecificClass1 : IInlineContainer

, .

:

  • , , , , 100% ,
  • , , , , ,

, , .

+1

( dynamic, IMO), :

Func<object, InlineCollection> GetInlinesFunction(Type type)
{
    string propertyName = ...;
    // ^ check whether type has a ContentPropertyAttribute and
    // retrieve its Name here, or null if there isn't one.
    if (propertyName == null)
        return null;
    var p = Expression.Parameter(typeof(object), "it");
    // The following creates a delegate that takes an object
    // as input and returns an InlineCollection (as long as
    // the object was at least of runtime-type "type".
    return Expression.Lambda<Func<object, InlineCollection>>(
        Expression.Property(
            Expression.Convert(p, type),
            propertyName),
        p).Compile();
}

-. Dictionary<Type, Func<object, InlineCollection>>. , , :

public static InlineCollection GetInlines(this TextElement element)
{
    Func<object, InlineCollection> f = GetCachedInlinesFunction(element.GetType());
    if (f != null)
        return f(element);
    else
        return null;
}

, ,

InlineCollection coll = someElement.GetInlines();

GetCachedInlinesFunction, , , try catch, , dynamic.

+1

, :

foreach (var control in controls) {
  var ic = control as IInlineContainer;
  if (ic != null) {
    DoSomething(ic.Inlines);
  }
}

, -, . ( ):

public class InlinesResolver {
  private object _target;
  public InlinesResolver(object target) {
    _target = target;
  }
  public bool HasInlines {
    get {
      return ResolveAttribute() != null;
    }
  }
  public InlineCollection Inlines {
    get {
      var propertyName = ResolveAttribute().Name;
      return (InlineCollection)
        _target.GetType().GetProperty(propertyName).GetGetMethod().Invoke(_target, new object[] { });
    }
  }
  private ContentPropertyAttribute ResolveAttribute() {
    var attrs = _target.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true);
    if (attrs.Length == 0) return null;
    return (ContentPropertyAttribute)attrs[0];
  }
}

:

foreach (var control in controls) {
  var ir = new InlinesResolver(control);
  if (ir.HasInlines) {
    DoSomething(ir.Inlines);
  }
}
+1

(, InlineParagraph, InlineBold ..), IInlineContainer, . , , , .

0

Api, , dynamic. Api , dynamic .

public void DoSomethingwithInlines(Paragraph p) {
    do(p);
}

public void DoSomethingwithInlines(Bolb b) {
    do(b);
}

private void do(dynamic d) {
    // access Inlines here, using c# dynamic
}
0

Source: https://habr.com/ru/post/1753906/


All Articles