I'm invoking a method using reflection but i want to check that the array of parameters i have are of the correct type before doing so. I've tried several methods like Type.IsAssignableFrom and other comparisons, but they still give false positives. For example when using an int as parameter to a function that accepts an enum.
The reference source seems to do exactly this check, is this exposed somewhere?
My current code looks like this:
private bool CheckTypeMatch(ParameterInfo[] methodParameters, Type[] callParameterTypes)
{
if (methodParameters.Length < callParameterTypes.Length)
{
return false;
}
for (int index = 0; index < callParameterTypes.Length; index++)
{
Type type = methodParameters[index].ParameterType;
if (callParameterTypes[index] != null && !type.IsAssignableFrom(callParameterTypes[index]) && !(type.IsEnum && System.Enum.GetUnderlyingType(type).IsAssignableFrom(callParameterTypes[index])))
{
return false;
}
}
return true;
}
I am worried there are more cases where the type of the parameter is actually compatible with the method but is being rejected by this function.
Thanks in advance!
Related
Help me make clean code. I need to do an action where I can pass a list of fields for validation. In my method I will input string, int, DataTime and display the name of the field where the error is contained. It can be done with many but I want best practices.
I wrote this
public override object Call()
{
DateTime now = DateTime.MinValue;
var validateArguments = ValidateArguments(now);
return validateArguments;
}
private static bool ValidateArguments(params object[] args)
{
for (var i = 0; i < args.Length; i++)
{
if (args[i] == null|| Convert.ToDateTime(args[i])==DateTime.MinValue)
{
StackTrace trace = new StackTrace();
// Get the method that called us
MethodBase info = trace.GetFrame(1).GetMethod();
// Get information on the parameter that is null so we can add its name to the exception
ParameterInfo param = info.GetParameters()[i];
InfoManager.MessageBox("Error in {0}", param.Name);
return false;
}
return true;
}
return false;
}
But this code produces following error:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
How do I fix it or write other clean code?
On this line ParameterInfo param = info.GetParameters()[i]; you are getting the parameters of your method Call() which has no parameters, and then indexing it with [i], this leads to an IndexOutOfRangeException. Perhaps you want to do this ParameterInfo param = info.GetParameters() and check that it is longer than 0 like this if (param.Length > 0). I cannot tell you what to do because I am not sure what you are trying to accomplish. Best of luck!
How can you drop items in a ICollection without knowing its TSource? I thought LINQ would be the best route for this, but I cannot figure out how it might be done with Reflection or any other method.
I am trying to implement this in a class that inherits System.Attribute so generic types are not permitted. And, the returned type information must be the same as the type information received on the value parameter of MutateValue. So obviously, generic would be a joy in this case, but is there another solution.
using System;
using System.Collections;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Dado.ComponentModel.DataMutations
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ApplyMaxLengthStackAttribute : MutationAttribute
{
private int _maxLength = -1;
public object Mutate(object value, int maxLength, IMutationContext context = null)
{
_maxLength = maxLength;
return Mutate(value, context);
}
protected override object MutateValue(object value, IMutationContext context)
{
if (value != null) {
if (
value is string valueAsString &&
(
_maxLength > -1 || TryGetStringLengthAttributeValue(context, out _maxLength)
) &&
valueAsString.Length > _maxLength
) {
value = valueAsString.Substring(0, _maxLength);
}
else if (
value is ICollection valueAsCollection &&
(
_maxLength > -1 || TryGetMaxLengthAttributeValue(context, out _maxLength)
) &&
valueAsCollection.Count > _maxLength
) {
// Error CS1061
value = valueAsCollection.Take(_maxLength);
}
}
_maxLength = -1;
return value;
}
private bool TryGetStringLengthAttributeValue(IMutationContext context, out int maxLength)
{
var attribute = context?.Attributes.OfType<StringLengthAttribute>().FirstOrDefault();
if (attribute != null) {
maxLength = attribute.MaximumLength;
return true;
}
return TryGetMaxLengthAttributeValue(context, out maxLength);
}
private bool TryGetMaxLengthAttributeValue(IMutationContext context, out int maxLength)
{
var attribute = context?.Attributes.OfType<MaxLengthAttribute>().FirstOrDefault();
if (attribute != null) {
maxLength = attribute.Length;
return true;
}
maxLength = -1;
return false;
}
}
}
You will see this code will not compile and throws this error:
Error CS1061: 'ICollection' does not contain a definition for 'Take' and no extension method 'Take' accepting a first argument of type 'ICollection' could be found (are you missing a using directive or an assembly reference?)
This class is an extension to the Dado.ComponentModel.Mutations project. Definitions for other Dado.ComponentModel.Mutations member definitions should be sought there.
dynamic to the rescue!
value = Enumerable.Take((dynamic)value, _maxLength);
There are few valid reasons to use dynamic in a language with such a powerful type system as C#, but it does have its occasional shortcomings, where the language designers for whatever reason (usually lack of time/resources) didn’t implement some way to express something that should be valid to express and possible for them to implement. However, when you hit what appears to be a rather arbitrary bump (or wall) that is simply an arbitrary limitation of the language, I think that is a perfectly valid reason to briefly abandon the type system, which is basically what dynamic lets you do. I believe you have hit one of those arbitrary limitations and you are perfectly justified in using dynamic in this situation.
If you want to make your code a little more “safe” or fool proof (should some collection that doesn’t implement IEnumerable<> get passed in), you can amend your else if to check if value implements IEnumerable<> (which you’ll have to do via reflection, unfortunately)
Many extension methods in LINQ work on type IEnumerable<T> rather than IEnumerable. Take is one such method. Try casting to IEnumerable<object> like this:
valueAsCollection.Cast<object>().Take(_maxLength)
I am trying to generalize some code using a C# generics. I ran into a problem on the line:
T defaultValue = (intList.Count == 1) ? intList[0] : GetDefaultValue(typeParameter);
with the error:
Error CS0173 Type of conditional expression cannot be determined because there is no implicit conversion between 'T [PathToFile(2035)]' and 'T [PathToFile(2058)]'
Below is the entire code:
private void ParseMultIndiciedList<T>(int count, params List<T>[] lists)
{
foreach (List<T> intList in lists)
{
//intList.Count should never be bigger than count
if (intList.Count > count)
{
throw new Exception("Multi-variable argument indicies do not match");
}
//If only 1 variable is assigned to the list, it is considered the default
Type typeParameter = typeof(T);
T defaultValue = (intList.Count == 1) ? intList[0] : GetDefaultValue(typeParameter);
int intListIntialCount = intList.Count;
if (intList.Count < count)
{
for (int countOffset = 0; countOffset < count-intListIntialCount; countOffset++)
{
intList.Add(defaultValue);
}
}
}
}
private T GetDefaultValue<T>(Type inputType)
{
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Int32:
return (T) Convert.ChangeType(-1, typeof (T));
}
return default(T);
}
I'm having trouble understand what the problem in the compiler is pertaining to the Type object, if I've directed a Type out of the first generic call in ParseMultiIndiciedList shouldn't I be Ok in the second generic method call to GetDefaultValue?
This significantly reduced code reproduces the same issue:
private void Foo<T>(List<T> intList)
{
Type typeParameter = typeof(T);
T defaultValue = (intList.Count == 1) ? intList[0] : GetDefaultValue(typeParameter);
}
private T GetDefaultValue<T>(Type inputType)
{
return default(T);
}
And yields not one, but two compiler errors (just as the original code did). The relevant one, apart from the one you mention in your question:
CS0411 The type arguments for method GetDefaultValue<T>(Type) cannot be inferred from the usage. Try specifying the type arguments explicitly.
This is because the compiler can't infer what T you want to pass to GetDefaultValue<T>() (because it's not used as a parameter type). Even though both are called T, they have no relation whatsoever, since they're specified on separate methods.
So in order to fix this, you'll have to pass the type argument for T explicitly:
GetDefaultValue<T>(typeParameter);
Now you tell that GetDefaultValue()'s generic argument T should be fed from Foo()'s T.
The error would be clearer if you'd rename the Ts:
private void Foo<TListItem>(List<TListItem> intList)
private TValue GetDefaultValue<TValue>(Type inputType)
Now the error would read:
Type of conditional expression cannot be determined because there is no implicit conversion between 'TListItem' and 'TValue '
And it'd be clear that they're two totally unrelated generic parameters.
But I'd consider a redesign altogether. The error in your question mentions that you're at line 2000 (!) of this code file, which is a maintenance disaster waiting to happen.
Put these two methods in their own class, and give it one type parameter. Then GetDefaultValue() can lose the T and use that of its containing class:
public class MultIndicedListParser<T>
{
public void Parse(int count, params List<T>[] lists)
{
// ...
}
private T GetDefaultValue(Type inputType)
{
// ...
}
}
The type parameter T in ParseMultIndiciedList<T> declaration is independent of type parameter T in GetDefaultValue<T> declaration. Therefore you need to explicitly pass type when calling GetDefaultValue from ParseMultIndiciedList:
T defaultValue = (intList.Count == 1) ? intList[0] : GetDefaultValue<T>(typeParameter);
^^^
The error you are getting shows that it's confused about what the return type of function might be in else part. If you add type information while calling that function it should be fine.
T defaultValue = (intList.Count == 1) ? intList[0] : GetDefaultValue<T>(typeParameter);
The above change should resolve the issue.
I was wondering if there was some existing logic to obtain the name (or any other relevant information) about the definition of a parameter in its containing method/constructor signature by looking at an invocation of that particular method/constructor. Basically, I just want to be able to get a default name for a variable that will be passed as an argument to the invocation. So, if a method if defined as such:
public void Foo(object firstParam, object secondParam, object thirdParam)
I would want to be able to say that the second argument of the following invocation
object bar = null;
this.Foo(null, bar, null)
is expected to have the name "secondParam". Basically, I just want to relate an argument to the original parameter whose "spot" it occupies in the invocation.
I am asking if any util methods that I am not aware of already exist within Roslyn, as there are some more complex scenarios to handle, such as named or optionnal arguments. The solution I've come up with in the meantime should covers some cases, but probably not all (especially params, which should require some more specialized logic to handle). Here's what I have so far:
private IEnumerable<IdentifierNameSyntax> GetParameterNamesFromArgumentList(ArgumentListSyntax argumentList, SyntaxNodeAnalysisContext context)
{
var arguments = argumentList.Arguments;
var parameters = argumentList.Parent.GetSymbolOrDeclaredAs<IMethodSymbol>(context)?.Parameters;
if (parameters != null)
{
var index = 0;
foreach (var parameter in parameters)
{
var argument = index < arguments.Count ? arguments[index] : null;
if (argument != null && argument.NameColon == null)
{
yield return SyntaxFactory.IdentifierName(parameter.Name);
}
else if (argument != null)
{
yield return argument.NameColon.Name;
}
index++;
}
}
}
I could be using DeclaringSyntaxReferenceson the method symbol, but I think that just having the names from the IParameterSymbol suited my needs well enough. Again, if this kind of logic is already implemented anywhere else, I'd rather use it. If not, well, feel free to tell me what you think of the problem.
Sadly I don't think there is a good public way to do this. See Roslyn's internal DetermineParameter helper for something that might help.
I am porting a small program of mine from Typescript to C#. In the original program I had a lot of checks like this:
var array: T[] = [];
if (!array[1234]) { // do something }
which basically checks if 1234 is a wrong index (undefined) or if the item has been specifically set to null or false by me.
Now when porting this to C# I'm basically replacing T[] with List<T> but I don't know what is the quickest way to perform this check, since if I use an invalid index I get an exception.
So my question is, what is the best way to check for both invalid index and index of a null or false item?
To check if the requested index is within the bounds you need to check myIndex < myList.Count.
If T is bool, you can do !myList[ix] like you are already doing.
In .NET, since bool is not nullable, you do not need to check if it is null. However, if T is nullable or a Nullable<T> i.e. bool?, you still need to do the == null check and also check if it isn't false.
If you are working in .NET 3.5 or above, you can write an extension method to make this a little easier on you. Here is something I whipped up to handle almost all cases (maybe all?).
public static class ListExtensions
{
public static bool ElementIsDefined<T>(this List<T> list, int index)
{
if (index < 0 || index >= list.Count)
return false;
var element = list[index];
var type = typeof (T);
if (Nullable.GetUnderlyingType(type) != null)
{
if (element is bool?)
return (element as bool?).Value;
return element != null;
}
var defaultValue = default(T);
// Use default(T) to actually get a value to check against.
// Using the below line to create the default when T is "object"
// causes a false positive to be returned.
return !EqualityComparer<T>.Default.Equals(element, defaultValue);
}
}
Quick overview of what this does:
Checks if the index is within the bounds.
Checks if the Type is Nullable (bool?, int?, etc.)
If it is, then we have to double check if the type is bool? so we can correctly determine if false was supplied and return based on that.
Then determines if the actual value out of the array is the default value. If it's bool, default is false, int is 0, and any references types are null.
You can call it like this:
var listBool = new List<bool?>();
listBool.Add(true);
listBool.Add(false);
listBool.Add(null);
listBool.ElementIsDefined(0) // returns true
listBool.ElementIsDefined(1) // returns false
listBool.ElementIsDefined(2) // returns false
Now quick note, this is not going to be lightning fast. It can be split up to handle different types of List<T> objects though so you can remove or add logic based on if you needed to create a similar method for List<int> or List<MyClass> and so on, but because I defined it as using a List<T> this method will show up for all Lists.
Now that you're using C# and have access to the BCL, you should use a Dictionary for this kind of thing. This allows you to efficiently add indices and check whether they're present.
For example, assuming T was string:
var s = new Dictionary<int, string>();
s[1234] = "Hello";
s[9999] = "Invalid";
var firstIndexCheck = s.ContainsKey(1234) && s[1234] != "Invalid"; // true
var secondIndexCheck = s.ContainsKey(9999) && s[9999] != "Invalid"; // false
For checking correct index just check that it is less then array/list size.
int index = 1234;
List<T> list = new List<T>();
if (index < list.Count) {
}
For checking for null combine index checking and null checking:
index < list.Count && list[index] != null
(in case if you have array of reference types)