RuntimeBinderException - C # .NET 4 Динамическое ключевое слово - Помогите мне понять, почему метод не соответствует - PullRequest
3 голосов
/ 24 мая 2010

Я создал стандартную систему конфигурации для HttpModule, которая позволяет подключаемые инспекторы заголовков HTTP.Для справки вот базовая схема кода - этого должно быть достаточно, чтобы понять, что я делаю:

public interface IHttpHeaderInspectingAuthenticatorFactory<T>
    where T: HttpHeaderInspectingAuthenticatorConfigurationElement
{
    IHttpHeaderInspectingAuthenticator<T> Construct(T config);
}

public class BasicAuthenticationInspectingAuthenticatorFactory :
    IHttpHeaderInspectingAuthenticator<BasicAuthenticationHeaderInspectorConfigurationElement>
{
    public IHttpHeaderInspectingAuthenticator<BasicAuthenticationHeaderInspectorConfigurationElement> Construct(BasicAuthenticationHeaderInspectorConfigurationElement config)
    {
        return new BasicAuthenticationInspectingAuthenticator(config);
    }
}

public class BasicAuthenticationInspectingAuthenticator : HttpHeaderInspectingAuthenticatorBase<BasicAuthenticationHeaderInspectorConfigurationElement>
{
    internal BasicAuthenticationInspectingAuthenticator(BasicAuthenticationHeaderInspectorConfigurationElement config) 
        : base(config) {}

 //snip -- IHttpHeaderInspectingAuthenticator<T> and IHttpHeaderInspectingAuthenticator implementation
}


public class BasicAuthenticationHeaderInspectorConfigurationElement : HttpHeaderInspectingAuthenticatorConfigurationElement
{
 //extra properties
}


public class HttpHeaderInspectingAuthenticatorConfigurationElement : ConfigurationElement
{
 protected override void PostDeserialize()
 {
     base.PostDeserialize();

     //simple verification of info supplied in config
     var t = Type.GetType(Factory);
     if (null == t)
         throw new ConfigurationErrorsException(String.Format("The factory type specified [{0}] cannot be found - check configuration settings", Factory ?? ""));

     if (!typeof(IHttpHeaderInspectingAuthenticatorFactory<>).IsGenericInterfaceAssignableFrom(t))
         throw new ConfigurationErrorsException(String.Format("The factory type specified [{0}] must derive from {1} - check configuration settings", Factory ?? "", typeof(IHttpHeaderInspectingAuthenticatorFactory<>).Name));

     var c = t.GetConstructor(Type.EmptyTypes);
     if (null == c)
         throw new ConfigurationErrorsException(String.Format("The factory type specified [{0}] must have a parameterless constructor - check configuration settings", Factory ?? ""));
 }

 [ConfigurationProperty("factory", IsRequired = true)]
 public string Factory
 {
  get { return (string)this["factory"]; }
  set { this["factory"] = value; }
 }

 //this allows us to use types derived from HttpHeaderInspectingAuthenticatorConfigurationElement
    protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
    {                    
        ConfigurationProperty property = new ConfigurationProperty(name, typeof(string), value);
        Properties.Add(property);
        base[property] = value;            
        return true;
    }

 public IHttpHeaderInspectingAuthenticator GetInspector()
 {
     dynamic factoryInstance = Activator.CreateInstance(Type.GetType(Factory));
     return factoryInstance.Construct(this);
 }
}

Теперь проблема возникает в вызове GetInspector - все экземплярыпредоставленное имя фабрики должно реализовывать IHttpHeaderInspectingAuthenticatorFactor <> (как указано в PostDeserialize).Следовательно, все они будут иметь метод Construct, где предоставленный тип T будет фактически фактическим типом класса, реализующего метод GetInspector ().Так, например, GetInspector будет вызываться для экземпляра BasicAuthenticationHeaderInspectorConfigurationElement - поэтому 'this' будет экземпляром BasicAuthenticationHeaderInspectorConfigurationElement.

Однако вызов Construct завершается неудачно.Он находит метод в порядке, но, видимо, тип параметра НЕ совпадает в зависимости от выданного исключения RuntimeBinderException.На основании CallSite.Target может показаться, что ожидаемый тип, который ожидает динамический прокси-сервер, - это HttpHeaderInspectingAuthenticatorConfigurationElement - передаваемый «this» - это BasicAuthenticationHeaderInspectorConfigurationElement, полученный из этой базы.

Так что же дает?Я не получаю что-то здесь?Я попытался передать (это как динамическое) или ((HttpHeaderInspectingAuthenticatorConfigurationElement) это) - но оба не удаются с одной и той же проблемой.

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'Redcated.Authentication.BasicAuthenticationInspectingAuthenticatorFactory.Construct(Redcated.Authentication.Configuration.BasicAuthenticationHeaderInspectorConfigurationElement)' has some invalid arguments
  at CallSite.Target(Closure , CallSite , Object , HttpHeaderInspectingAuthenticatorConfigurationElement )
  at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
  at Redcated.Authentication.Configuration.HttpHeaderInspectingAuthenticatorConfigurationElement.GetInspector() in D:\Users\ebrown\Documents\Visual Studio 2010\Projects\Utility\source\Authentication Library\Configuration\HttpHeaderInspectingAuthenticatorConfigurationElement.cs:line 79
  at Redcated.Authentication.Configuration.HttpHeaderInspectingAuthenticatorConfigurationElementCollection.<GetInspectors>b__0(HttpHeaderInspectingAuthenticatorConfigurationElement i) in D:\Users\ebrown\Documents\Visual Studio 2010\Projects\Utility\source\Authentication Library\Configuration\HttpHeaderInspectingAuthenticatorConfigurationElementCollection.cs:line 78
  at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
  at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
  at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
  at Redcated.Authentication.HttpHeaderInspectingAuthenticationModule.AuthenticateRequest(Object sender, EventArgs e) in D:\Users\ebrown\Documents\Visual Studio 2010\Projects\Utility\source\Authentication Library\HttpHeaderInspectingAuthenticationModule.cs:line 49
  at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
  at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

FYI - я обошел эту проблему, изменив архитектуру интерфейсовнемного:

public interface IHttpHeaderInspectingAuthenticatorFactory
{
    IHttpHeaderInspectingAuthenticator Construct(HttpHeaderInspectingAuthenticatorConfigurationElement config);
}

public interface IHttpHeaderInspectingAuthenticatorFactory<T> : IHttpHeaderInspectingAuthenticatorFactory
    where T: HttpHeaderInspectingAuthenticatorConfigurationElement
{
    IHttpHeaderInspectingAuthenticator<T> Construct(T config);
}

public abstract class HttpHeaderInspectingAuthenticatorFactoryBase<T> : IHttpHeaderInspectingAuthenticatorFactory<T>
            where T : HttpHeaderInspectingAuthenticatorConfigurationElement
{
    protected static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    public abstract IHttpHeaderInspectingAuthenticator<T> Construct(T config);

    public IHttpHeaderInspectingAuthenticator Construct(HttpHeaderInspectingAuthenticatorConfigurationElement config)
    {
        return Construct((T)config);
    }
}

 public class BasicAuthenticationInspectingAuthenticatorFactory :
    HttpHeaderInspectingAuthenticatorFactoryBase<BasicAuthenticationHeaderInspectorConfigurationElement>
{
    public override IHttpHeaderInspectingAuthenticator<BasicAuthenticationHeaderInspectorConfigurationElement> Construct(BasicAuthenticationHeaderInspectorConfigurationElement config)
    {
        return new BasicAuthenticationInspectingAuthenticator(config);
    }
}

И затем, конечно, вызов GetInspector становится

public IHttpHeaderInspectingAuthenticator GetInspector()
{
    var factoryInstance = (IHttpHeaderInspectingAuthenticatorFactory)Activator.CreateInstance(Type.GetType(Factory));
    return factoryInstance.Construct(this);
}

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

Спасибо!

Ответы [ 2 ]

3 голосов
/ 14 марта 2011

Я думаю, вам нужно разыграть this как динамическое. например factoryInstance.Construct((dynamic)this); Я думаю, что проблема в том, что динамический вызов по умолчанию использует информацию времени компиляции для подписи кэшированного делегата (Target).

Поскольку ваша реализация динамического вызова находится в базовом классе, это то, что она использует для подписи, но, поскольку ваша подпись с поздней привязкой является подклассом, вы не можете перейти HttpHeaderInspectingAuthenticatorConfigurationElement -> BasicAuthenticationHeaderInspectorConfigurationElement, поэтому преобразовывая аргумент в динамический, вы сообщаете DLR, что тип аргументов определяется во время выполнения.

0 голосов
/ 05 августа 2010

Вы говорите, что:

 dynamic factoryInstance = Activator.CreateInstance(Type.GetType(Factory));
 dynamic parameter = this;
 return factoryInstance.Construct(parameter); 

Не сработало?

...