public bool IsList(object value)
{
Type type = value.GetType();
// Check if type is a generic list of any type
}
What's the best way to check if the given object is a list, or can be cast to a list?
For you guys that enjoy the use of extension methods:
public static bool IsGenericList(this object o)
{
var oType = o.GetType();
return (oType.IsGenericType && (oType.GetGenericTypeDefinition() == typeof(List<>)));
}
So, we could do:
if(o.IsGenericList())
{
//...
}
using System.Collections;
if(value is IList && value.GetType().IsGenericType) {
}
bool isList = o.GetType().IsGenericType
&& o.GetType().GetGenericTypeDefinition() == typeof(IList<>));
public bool IsList(object value) {
return value is IList
|| IsGenericList(value);
}
public bool IsGenericList(object value) {
var type = value.GetType();
return type.IsGenericType
&& typeof(List<>) == type.GetGenericTypeDefinition();
}
Here's an implementation that works in .NET Standard, and works against interfaces:
public static bool ImplementsGenericInterface(this Type type, Type interfaceType)
{
return type
.GetTypeInfo()
.ImplementedInterfaces
.Any(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == interfaceType);
}
And here are the tests (xunit):
[Fact]
public void ImplementsGenericInterface_List_IsValidInterfaceTypes()
{
var list = new List<string>();
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IList<>)));
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IEnumerable<>)));
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IReadOnlyList<>)));
}
[Fact]
public void ImplementsGenericInterface_List_IsNotInvalidInterfaceTypes()
{
var list = new List<string>();
Assert.False(list.GetType().ImplementsGenericInterface(typeof(string)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(IDictionary<,>)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(IComparable<>)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(DateTime)));
}
if(value is IList && value.GetType().GetGenericArguments().Length > 0)
{
}
Based on Victor Rodrigues' answer, we can devise another method for generics. In fact, the original solution can be reduced to only two lines:
public static bool IsGenericList(this object Value)
{
var t = Value.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
}
public static bool IsGenericList<T>(this object Value)
{
var t = Value.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<T>);
}
I'm using the following code:
public bool IsList(Type type) => type.IsGenericType && (
(type.GetGenericTypeDefinition() == typeof(List<>))
|| (type.GetGenericTypeDefinition() == typeof(IList<>))
);
Probably the best way would be to do something like this:
IList list = value as IList;
if (list != null)
{
// use list in here
}
This will give you maximum flexibility and also allow you to work with many different types that implement the IList interface.
Related
I have a class named VerseRangeReference that has the properties Chapter, FirstVerse and LastVerse.
I have decorated it with a TypeConverterAttribute [TypeConverter(typeof(VerseRangeReferenceConverter))]
I have an action on a controller like this
public Task<ViewResult> Verses(VerseRangeReference[] verses)
But the value of verses is always a single element with the value null. Here is my type converter
public class VerseRangeReferenceConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (value.GetType() == typeof(string))
{
string source = (string)value;
return VerseRangeReference.ParseMultiple(source);
}
return null;
}
}
The result of VerseRangeReference.ParseMultiple(source) is a valid array of instances of VerseRange.
I had to implement a custom model binder. If someone can think of a way to do this with a TypeConverter then I will accept that answer instead because model binders are more complicated.
public class VerseRangeReferenceArrayModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
string modelName = bindingContext.ModelName;
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult != ValueProviderResult.None)
{
VerseRangeReference[] verseRangeReferences = VerseRangeReference.ParseMultiple(valueProviderResult.FirstValue);
bindingContext.Result = ModelBindingResult.Success(verseRangeReferences);
}
return Task.CompletedTask;
}
}
public class VerseRangerReferenceArrayModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(VerseRangeReference[]))
return new BinderTypeModelBinder(typeof(VerseRangeReferenceArrayModelBinder));
return null;
}
}
This must be registered.
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new VerseRangerReferenceArrayModelBinderProvider());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
You can use a type converter to bind a comma separated string to a sequence of values. However, the type converter should convert from the string to the sequence directly. This means that the type converter should be configured for something like IEnumerable<T> or T[]. To simplify matters I will continue my explanation for IEnumerable<int> but if you want to use arrays instead you should just make sure that the type converter converts to an array instead of something that implements IEnumerable<T>.
You can configure a type converter for IEnumerable<int> using TypeDescriptor.AddAttributes:
TypeDescriptor.AddAttributes(
typeof(IEnumerable<int>),
new TypeConverterAttribute(typeof(EnumerableIntTypeConverter)));
This configures EnumerableIntTypeConverter as a type converter that can convert IEnumerable<int>.
This call has to be made when the process starts and in the case of ASP.NET Core this can conveniently be done in the Startup.Configure method.
Here is the EnumerableIntTypeConverter that converts the comma separated string of numbers to a list of ints:
internal class EnumerableIntTypeConverter : TypeConverter
{
private const char Separator = ',';
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
=> sourceType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (!(value is string #string))
throw new NotSupportedException($"{GetType().Name} cannot convert from {(value != null ? value.GetType().FullName : "(null)")}.");
if (#string.Length == 0)
return Enumerable.Empty<int>();
var numbers = new List<int>();
var start = 0;
var end = GetEnd(#string, start);
while (true)
{
if (!int.TryParse(
#string.AsSpan(start, end - start),
NumberStyles.AllowLeadingSign,
culture,
out var number))
throw new FormatException($"{GetType().Name} cannot parse string with invalid format.");
numbers.Add(number);
if (end == #string.Length)
break;
start = end + 1;
end = GetEnd(#string, start);
}
return numbers;
}
private static int GetEnd(string #string, int start)
{
var end = #string.IndexOf(Separator, start);
return end >= 0 ? end : #string.Length;
}
}
The parsing uses System.Memory to avoid allocating a new string for each number in the list. If your framework doesn't have the int.TryParse overload that accepts a Span<char> you can use string.Substring instead.
I have a function that creates regular Objects of a same type and I cannot avoid that step.
When I use List.addAll(*) I will get many "Duplications" that are not equal in sense of Objectivity.
I have a very bad coded solution and want to ask if there could be a better or more effective one maybe with Java-Util-functions and defining a Comparator for that single intermezzo?
Here is my bad smell:
private void addPartial(List<SeMo_WikiArticle> allnewWiki, List<SeMo_WikiArticle> newWiki) {
if(allnewWiki.isEmpty())
allnewWiki.addAll(newWiki);
else{
for(SeMo_WikiArticle nn : newWiki){
boolean allreadyIn = false;
for(SeMo_WikiArticle oo : allnewWiki){
if(nn.getID()==oo.getID())
allreadyIn= true;
}
if(!allreadyIn)
allnewWiki.add(nn);
}
}
}
Any Ideas?
Add an override function of equals() into class SeMo_WikiArticle :
class SeMo_WikiArticle {
// assuming this class has two properties below
int id;
String name;
SeMo_WikiArticle(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
// implement your own comparison policy
// here is an example
if (obj instanceof SeMo_WikiArticle) {
SeMo_WikiArticle sw = (SeMo_WikiArticle)obj;
if (this.id == sw.id && (this.name == sw.name || this.name.equals(sw.name))) {
return true;
}
}
return false;
}
}
After that you can use contains() to judge if the list has already contains the specific object of SeMo_WikiArticle.
Here is the code:
private void addPartial(List<SeMo_WikiArticle> allnewWiki, List<SeMo_WikiArticle> newWiki) {
for (SeMo_WikiArticle sw : newWiki) {
if (!allnewWiki.contains(sw)) {
allnewWiki.add(sw);
}
}
}
I have an existing application that I am modifying to use Autofac Property Injection. It seems regardless of which method I use to register my types with properties, the properties are always null unless they have public setters. With other IoC containers (e.g. Structuremap) it's possible to scope the setter internal and make it available using the InternalsVisibleTo attribute on the assembly. This would seem nice to restrict clients from modifying the assignment.
Is this possible with Autofac? Or is there another approach when working with property injection to keep the assignments secure?
I've tried using reflection with PropertiesAutoWired() as well as resolving .WithParameter() from my WebApi Global.asax - specifying the specific parameter to be set with no success as an internal setter.
[assembly: InternalsVisibleTo("MyWebAPI.dll")]
[assembly: InternalsVisibleTo("Autofac.dll")]
[assembly: InternalsVisibleTo("Autofac.Configuration.dll")]
namespace My.Namespace
{
public class BaseContext
{
public MyPublicClass _dbHelper { get; internal set; }
public BaseContext()
{
}
protected string DbConnectionString
{
get
{
return _dbHelper.DbConn; //<-Always null unless setter is public
}
}
}
}
You cannot inject internal setters with autofac, because the AutowiringPropertyInjector class is only looking for public properties (see source).
However a logic in the AutowiringPropertyInjector is very simple so you can create your own version which does injection for non public properties:
public static class AutowiringNonPublicPropertyInjector
{
public static void InjectProperties(IComponentContext context,
object instance, bool overrideSetValues)
{
if (context == null)
throw new ArgumentNullException("context");
if (instance == null)
throw new ArgumentNullException("instance");
foreach (
PropertyInfo propertyInfo in
//BindingFlags.NonPublic flag added for non public properties
instance.GetType().GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
Type propertyType = propertyInfo.PropertyType;
if ((!propertyType.IsValueType || propertyType.IsEnum) &&
(propertyInfo.GetIndexParameters().Length == 0 &&
context.IsRegistered(propertyType)))
{
//Changed to GetAccessors(true) to return non public accessors
MethodInfo[] accessors = propertyInfo.GetAccessors(true);
if ((accessors.Length != 1 ||
!(accessors[0].ReturnType != typeof (void))) &&
(overrideSetValues || accessors.Length != 2 ||
propertyInfo.GetValue(instance, null) == null))
{
object obj = context.Resolve(propertyType);
propertyInfo.SetValue(instance, obj, null);
}
}
}
}
}
And now you can use this class in the OnActivated event
var builder = new ContainerBuilder();
builder.RegisterType<MyPublicClass>();
builder.RegisterType<BaseContext>()
.OnActivated(args =>
AutowiringNonPublicPropertyInjector
.InjectProperties(args.Context, args.Instance, true));
However the above listed solution now injects all kind of properties so even private and protected ones so you may need to extend it with some additional checks to make sure that you will only inject the properties what you would expect.
I'm using a solution like this:
builder.RegisterType<MyPublicClass>();
builder.RegisterType<BaseContext>()
.OnActivating(CustomPropertiesHandler);
With a handler like this:
//If OnActivated: Autofac.Core.IActivatedEventArgs
public void CustomPropertiesHandler<T>(Autofac.Core.IActivatingEventArgs<T> e)
{
var props = e.Instance.GetType()
.GetTypeInfo().DeclaredProperties //Also "private prop" with "public set"
.Where(pi => pi.CanWrite) //Has a set accessor.
//.Where(pi => pi.SetMethod.IsPrivate) //set accessor is private
.Where(pi => e.Context.IsRegistered(pi.PropertyType)); //Type is resolvable
foreach (var prop in props)
prop.SetValue(e.Instance, e.Context.Resolve(prop.PropertyType), null);
}
Since both IActivatingEventArgs and IActivatedEventArgs has instance and context, you might want to use wrapping methods that uses those parameters on CustomPropertiesHandler instead.
Also we can write #nemesv implementation as an extension method.
public static class AutofacExtensions
{
public static void InjectProperties(IComponentContext context, object instance, bool overrideSetValues)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (instance == null)
{
throw new ArgumentNullException(nameof(instance));
}
foreach (var propertyInfo in instance.GetType().GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
var propertyType = propertyInfo.PropertyType;
if ((!propertyType.IsValueType || propertyType.IsEnum) && (propertyInfo.GetIndexParameters().Length == 0) && context.IsRegistered(propertyType))
{
var accessors = propertyInfo.GetAccessors(true);
if (((accessors.Length != 1) ||
!(accessors[0].ReturnType != typeof(void))) &&
(overrideSetValues || (accessors.Length != 2) ||
(propertyInfo.GetValue(instance, null) == null)))
{
var obj = context.Resolve(propertyType);
propertyInfo.SetValue(instance, obj, null);
}
}
}
}
public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InjectPropertiesAsAutowired<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registration)
{
return registration.OnActivated(args => InjectProperties(args.Context, args.Instance, true));
}
To Use;
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<StartupConfiguration>().As<IStartupConfiguration>().AsSelf().InjectPropertiesAsAutowired().AsImplementedInterfaces().SingleInstance();
}
Current version of Autofac defined optional IPropertySelector parameter for PropertiesAutowired which is used to filter out injectable properties.
default implementation for IPropertySelector is DefaultPropertySelector, which filters non public properties.
public virtual bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
if (!propertyInfo.CanWrite || propertyInfo.SetMethod?.IsPublic != true)
{
return false;
}
....
}
define custom IPropertySelector which allows injection to non public properties
public class AccessRightInvariantPropertySelector : DefaultPropertySelector
{
public AccessRightInvariantPropertySelector(bool preserveSetValues) : base(preserveSetValues)
{ }
public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
if (!propertyInfo.CanWrite)
{
return false;
}
if (!PreserveSetValues || !propertyInfo.CanRead)
{
return true;
}
try
{
return propertyInfo.GetValue(instance, null) == null;
}
catch
{
// Issue #799: If getting the property value throws an exception
// then assume it's set and skip it.
return false;
}
}
}
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.PropertiesAutowired(new AccessRightInvariantPropertySelector(true));
Alternatively
Install
PM> Install-Package Autofac.Core.NonPublicProperty
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.AutoWireNonPublicProperties();
I have my web api and I added the web api help pages to auto-generate my documentation. It's working great for methods where my parameters are listed out, but I have a method like this:
public SessionResult PostLogin(CreateSessionCommand request)
And, on my help page, it is only listing the command parameter in the properties section. However, in the sample request section, it lists out all of the properties of my CreateSessionCommand class.
Parameters
Name | Description | Additional information
request | No documentation available. | Define this parameter in the request body.
I would like it instead to list all of the properties in my CreateSessionCommand class. Is there an easy way to do this?
So, I managed to devise a workaround for this problem, in case anyone is interested.
In HelpPageConfigurationExtensions.cs I added the following extension method:
public static void AlterApiDescription(this ApiDescription apiDescription, HttpConfiguration config)
{
var docProvider = config.Services.GetDocumentationProvider();
var addParams = new List<ApiParameterDescription>();
var removeParams = new List<ApiParameterDescription>();
foreach (var param in apiDescription.ParameterDescriptions)
{
var type = param.ParameterDescriptor.ParameterType;
//string is some special case that is not a primitive type
//also, compare by full name because the type returned does not seem to match the types generated by typeof
bool isPrimitive = type.IsPrimitive || String.Compare(type.FullName, typeof(string).FullName) == 0;
if (!isPrimitive)
{
var properties = from p in param.ParameterDescriptor.ParameterType.GetProperties()
let s = p.SetMethod
where s.IsPublic
select p;
foreach (var property in properties)
{
var documentation = docProvider.GetDocumentation(new System.Web.Http.Controllers.ReflectedHttpParameterDescriptor()
{
ActionDescriptor = param.ParameterDescriptor.ActionDescriptor,
ParameterInfo = new CustomParameterInfo(property)
});
addParams.Add(new ApiParameterDescription()
{
Documentation = documentation,
Name = property.Name,
Source = ApiParameterSource.FromBody,
ParameterDescriptor = param.ParameterDescriptor
});
}
//since this is a complex type, select it to be removed from the api description
removeParams.Add(param);
}
}
//add in our new items
foreach (var item in addParams)
{
apiDescription.ParameterDescriptions.Add(item);
}
//remove the complex types
foreach (var item in removeParams)
{
apiDescription.ParameterDescriptions.Remove(item);
}
}
And here is the Parameter info instanced class I use
internal class CustomParameterInfo : ParameterInfo
{
public CustomParameterInfo(PropertyInfo prop)
{
base.NameImpl = prop.Name;
}
}
Then, we call the extension in another method inside the extensions class
public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId)
{
object model;
string modelId = ApiModelPrefix + apiDescriptionId;
if (!config.Properties.TryGetValue(modelId, out model))
{
Collection<ApiDescription> apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions;
ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase));
if (apiDescription != null)
{
apiDescription.AlterApiDescription(config);
HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
model = GenerateApiModel(apiDescription, sampleGenerator);
config.Properties.TryAdd(modelId, model);
}
}
return (HelpPageApiModel)model;
}
The comments that are used for this must be added to the controller method and not the properties of the class object. This might be because my object is part of an outside library
this should go as an addition to #Josh answer. If you want not only to list properties from the model class, but also include documentation for each property, Areas/HelpPage/XmlDocumentationProvider.cs file should be modified as follows:
public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
{
ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;
if (reflectedParameterDescriptor != null)
{
if (reflectedParameterDescriptor.ParameterInfo is CustomParameterInfo)
{
const string PropertyExpression = "/doc/members/member[#name='P:{0}']";
var pi = (CustomParameterInfo) reflectedParameterDescriptor.ParameterInfo;
string selectExpression = String.Format(CultureInfo.InvariantCulture, PropertyExpression, pi.Prop.DeclaringType.FullName + "." + pi.Prop.Name);
XPathNavigator methodNode = _documentNavigator.SelectSingleNode(selectExpression);
if (methodNode != null)
{
return methodNode.Value.Trim();
}
}
else
{
XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor);
if (methodNode != null)
{
string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName));
if (parameterNode != null)
{
return parameterNode.Value.Trim();
}
}
}
}
return null;
}
and CustomParameterInfo class should keep property info as well:
internal class CustomParameterInfo : ParameterInfo
{
public PropertyInfo Prop { get; private set; }
public CustomParameterInfo(PropertyInfo prop)
{
Prop = prop;
base.NameImpl = prop.Name;
}
}
This is currently not supported out of the box. Following bug is kind of related to that:
http://aspnetwebstack.codeplex.com/workitem/877
In C++/CLI code I need to check if the type is a specific generic type. In C# it would be:
public static class type_helper {
public static bool is_dict( Type t ) {
return t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IDictionary<,>);
}
}
but in cpp++\cli it does not work the same way, compiler shows the syntax error:
class type_helper {
public:
static bool is_dict( Type^ t ) {
return t->IsGenericType && t->GetGenericTypeDefinition()
== System::Collections::Generic::IDictionary<,>::typeid;
}
};
The best way I find is compare strings like this:
class type_helper {
public:
static bool is_dict( Type^ t ) {
return t->IsGenericType
&& t->GetGenericTypeDefinition()->Name == "IDictionary`2";
}
};
Does anybody know the better way?
PS:
Is it limitation of typeof (typeid) in c++\cli or I do not know "correct" systax?
You could write:
return t->IsGenericType
&& t->GetGenericTypeDefinition() == System::Collections::Generic::IDictionary<int,int>::typeid->GetGenericTypeDefinition();