C# 可以将 Object 附加到方法调用而不将其作为参数吗?
C# Possible to attach an Object to a method call without having it as a parameter?
我正在设计一个具有 AOP 架构 (postsharp) 的程序,它将拦截所有方法调用,但我需要一种方法来为每个调用附加一个 class。问题是我不想在每个方法调用中都显式传递 class 。那么有没有办法将 class 附加到 C# 中的方法调用?
例如,在 angular 中,我可以使用自定义拦截器将我想要的任何内容附加到 header 以用于每个拨出电话。这样可以减少重复代码。 C#中有这样的东西吗?
@Injectable()
export class CustomInterceptor implements HttpInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({ withCredentials: true });
return next.handle(request);
}
}
这是我在 C# 中的界面
public class Wrapper: IMyInterface
{
private IMyInterface_wrapped;
public Wrapper(IMyInterface caller)
{
_wrapped = caller;
}
public FOO GetUserStuff(string userName)
{
return _wrapped.GetUserStuff(req);
}
}
}
有没有办法可以像这样调用接口
var wrapper = new Wrapper(new MyInterface());
LoginRequest req = new LoginRequest <------ this needs to be attached to every single method call
{
ClientId = "ABCDEFG",
ClientSecret = "123456"
};
wrapper.GetUserStuff("Username", req); <------- My interface only takes one argument.
wrapper.GetUserStuff("UserName").append(req) <----of course this doesn't work either
有没有一种方法可以调用接口方法并将 object 附加到它而无需在接口中实际实现它?
您可以将其设为静态 class 并在需要时调用静态方法。或者如果你想让它像 Angular 中那样,你可以将它添加到管道中(启动配置方法):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (context, next) =>
{
LoginRequest req = new LoginRequest
{
ClientId = "ABCDEFG",
ClientSecret = "123456"
};
context.Response.Headers["ClientId"] = "ABCDEFG";
await next();
});
}
通过 DI 容器,您可以轻松地将 IOption<>
接口注入 class 构造函数:
public class Wrapper: IMyInterface
{
private IMyInterface_wrapped;
private MySettings _mySettings;
public Wrapper(IMyInterface caller, IOptions<MySettings> mySettings)
{
_wrapped = caller;
_mySettings = mySettings.Value;
}
private LoginRequest GetLoginRequest()
{
return new LoginRequest
{
ClientId = _mySettings.ClientId,
ClientSecret = _mySettings.ClientSecret
};
}
public FOO GetUserStuff(string userName)
{
return _wrapped.GetUserStuff(GetLoginRequest());
}
}
基本上您想要的是 - 每当调用 wrapper.GetUserStuff
方法时,LoginRequest
对象可用于 Wrapper
class 对象。
但是正如您在评论部分的回答,ClientId
和 ClientSecret
的值没有改变。然后,您可以避免每次都在外部创建 LoginRequest
对象并将其作为方法参数传递到内部的整个麻烦,只需在 内部 中创建 LoginRequest
对象 [= =14=] class -
public class Wrapper : IMyInterface
{
private IMyInterface _wrapped;
private LoginRequest _req;
public Wrapper(IMyInterface caller)
{
_wrapped = caller;
_req = new LoginRequest { ClientId = "ABCDEFG", ClientSecret = "123456" };
}
public int GetUserStuff(string userName)
{
return _wrapped.GetUserStuff(_req);
}
}
通常,您会将 ClientId
和 ClientSecret
值存储在其他地方(而不是对它们进行硬编码)并相应地读取它们。
而且,如果您无法从 Wrapper
class 访问 LoginRequest
class(可能位于单独的 layer/project没有所需的程序集引用),那么您可以像 ClientInfo
一样声明 class 并像 -
一样使用它
public class ClientInfo
{
public string UserName { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class Wrapper : IMyInterface
{
private IMyInterface _wrapped;
private ClientInfo _info;
public Wrapper(IMyInterface caller)
{
_wrapped = caller;
_info = new ClientInfo { ClientId = "ABCDEFG", ClientSecret = "123456" };
}
public int GetUserStuff(string userName)
{
_info.UserName = userName;
return _wrapped.GetUserStuff(_info);
}
}
然后 caller
可以从传递给它的 ClientInfo
创建 LoginRequest
对象。
要稍微改变@atiyar 的方法,您可以使用访问器。这是 HTTPAccessor 核心中使用的通用版本。 AsyncLocal 将为主线程设置一次,然后传播到任何衍生的线程。
public class GenericAccessor<T> where T : class
{
private static AsyncLocal<Holder<T>> _current = new AsyncLocal<Holder<T>>();
public T Value
{
get => _current.Value?.Context;
set
{
var holder = _current.Value;
if (holder != null)
{
// Clear current trapped in the AsyncLocals, as its done.
holder.Context = null;
}
if (value != null)
{
// Use an object indirection to hold the in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
_current.Value = new Holder<T> { Context = value };
}
}
}
private class Holder<T>
{
public T Context;
}
}
随着实施
public class ClientInfo
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class UserInfo: ClientInfo
{
public UserInfo(ClientInfo clientInfo)
{
this.ClientId = clientInfo.ClientId;
this.ClientSecret = clientInfo.ClientSecret;
}
public string UserName { get; set; }
}
public interface IClientInfoAccessor
{
ClientInfo ClientInfo { get; set; }
}
public class ClientInfoAccessor : GenericAccessor<ClientInfo>, IClientInfoAccessor
{
public ClientInfo ClientInfo{ get => Value; set => Value = value; }
}
public class Wrapper: IMyInterface
{
private IMyInterface _wrapped;
private IClientInfoAccessor _accessor;
public Wrapper(IMyInterface caller, IClientInfoAccessor accessor)
{
_wrapped = caller;
_accessor = accessor;
}
public int GetUserStuff(string userName)
{
var req = new UserInfo(_accessor.ClientInfo);
req.UserName = userName;
return _wrapped.GetUserStuff(req);
}
}
您需要做的就是为每个操作在中间件中设置 ClientInfo,您甚至可以在单例中的任何地方使用访问器。
我正在设计一个具有 AOP 架构 (postsharp) 的程序,它将拦截所有方法调用,但我需要一种方法来为每个调用附加一个 class。问题是我不想在每个方法调用中都显式传递 class 。那么有没有办法将 class 附加到 C# 中的方法调用?
例如,在 angular 中,我可以使用自定义拦截器将我想要的任何内容附加到 header 以用于每个拨出电话。这样可以减少重复代码。 C#中有这样的东西吗?
@Injectable()
export class CustomInterceptor implements HttpInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({ withCredentials: true });
return next.handle(request);
}
}
这是我在 C# 中的界面
public class Wrapper: IMyInterface
{
private IMyInterface_wrapped;
public Wrapper(IMyInterface caller)
{
_wrapped = caller;
}
public FOO GetUserStuff(string userName)
{
return _wrapped.GetUserStuff(req);
}
}
}
有没有办法可以像这样调用接口
var wrapper = new Wrapper(new MyInterface());
LoginRequest req = new LoginRequest <------ this needs to be attached to every single method call
{
ClientId = "ABCDEFG",
ClientSecret = "123456"
};
wrapper.GetUserStuff("Username", req); <------- My interface only takes one argument.
wrapper.GetUserStuff("UserName").append(req) <----of course this doesn't work either
有没有一种方法可以调用接口方法并将 object 附加到它而无需在接口中实际实现它?
您可以将其设为静态 class 并在需要时调用静态方法。或者如果你想让它像 Angular 中那样,你可以将它添加到管道中(启动配置方法):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (context, next) =>
{
LoginRequest req = new LoginRequest
{
ClientId = "ABCDEFG",
ClientSecret = "123456"
};
context.Response.Headers["ClientId"] = "ABCDEFG";
await next();
});
}
通过 DI 容器,您可以轻松地将 IOption<>
接口注入 class 构造函数:
public class Wrapper: IMyInterface
{
private IMyInterface_wrapped;
private MySettings _mySettings;
public Wrapper(IMyInterface caller, IOptions<MySettings> mySettings)
{
_wrapped = caller;
_mySettings = mySettings.Value;
}
private LoginRequest GetLoginRequest()
{
return new LoginRequest
{
ClientId = _mySettings.ClientId,
ClientSecret = _mySettings.ClientSecret
};
}
public FOO GetUserStuff(string userName)
{
return _wrapped.GetUserStuff(GetLoginRequest());
}
}
基本上您想要的是 - 每当调用 wrapper.GetUserStuff
方法时,LoginRequest
对象可用于 Wrapper
class 对象。
但是正如您在评论部分的回答,ClientId
和 ClientSecret
的值没有改变。然后,您可以避免每次都在外部创建 LoginRequest
对象并将其作为方法参数传递到内部的整个麻烦,只需在 内部 中创建 LoginRequest
对象 [= =14=] class -
public class Wrapper : IMyInterface
{
private IMyInterface _wrapped;
private LoginRequest _req;
public Wrapper(IMyInterface caller)
{
_wrapped = caller;
_req = new LoginRequest { ClientId = "ABCDEFG", ClientSecret = "123456" };
}
public int GetUserStuff(string userName)
{
return _wrapped.GetUserStuff(_req);
}
}
通常,您会将 ClientId
和 ClientSecret
值存储在其他地方(而不是对它们进行硬编码)并相应地读取它们。
而且,如果您无法从 Wrapper
class 访问 LoginRequest
class(可能位于单独的 layer/project没有所需的程序集引用),那么您可以像 ClientInfo
一样声明 class 并像 -
public class ClientInfo
{
public string UserName { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class Wrapper : IMyInterface
{
private IMyInterface _wrapped;
private ClientInfo _info;
public Wrapper(IMyInterface caller)
{
_wrapped = caller;
_info = new ClientInfo { ClientId = "ABCDEFG", ClientSecret = "123456" };
}
public int GetUserStuff(string userName)
{
_info.UserName = userName;
return _wrapped.GetUserStuff(_info);
}
}
然后 caller
可以从传递给它的 ClientInfo
创建 LoginRequest
对象。
要稍微改变@atiyar 的方法,您可以使用访问器。这是 HTTPAccessor 核心中使用的通用版本。 AsyncLocal 将为主线程设置一次,然后传播到任何衍生的线程。
public class GenericAccessor<T> where T : class
{
private static AsyncLocal<Holder<T>> _current = new AsyncLocal<Holder<T>>();
public T Value
{
get => _current.Value?.Context;
set
{
var holder = _current.Value;
if (holder != null)
{
// Clear current trapped in the AsyncLocals, as its done.
holder.Context = null;
}
if (value != null)
{
// Use an object indirection to hold the in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
_current.Value = new Holder<T> { Context = value };
}
}
}
private class Holder<T>
{
public T Context;
}
}
随着实施
public class ClientInfo
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class UserInfo: ClientInfo
{
public UserInfo(ClientInfo clientInfo)
{
this.ClientId = clientInfo.ClientId;
this.ClientSecret = clientInfo.ClientSecret;
}
public string UserName { get; set; }
}
public interface IClientInfoAccessor
{
ClientInfo ClientInfo { get; set; }
}
public class ClientInfoAccessor : GenericAccessor<ClientInfo>, IClientInfoAccessor
{
public ClientInfo ClientInfo{ get => Value; set => Value = value; }
}
public class Wrapper: IMyInterface
{
private IMyInterface _wrapped;
private IClientInfoAccessor _accessor;
public Wrapper(IMyInterface caller, IClientInfoAccessor accessor)
{
_wrapped = caller;
_accessor = accessor;
}
public int GetUserStuff(string userName)
{
var req = new UserInfo(_accessor.ClientInfo);
req.UserName = userName;
return _wrapped.GetUserStuff(req);
}
}
您需要做的就是为每个操作在中间件中设置 ClientInfo,您甚至可以在单例中的任何地方使用访问器。