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)
Related
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.
As far as I knew, Object.GetType() should never return null. (related discussion)
Dapper .Query() return private class DapperRow instances to be treated as dynamic objects. I found a strange thing: DapperRow's .GetType() return null.
Here's the sample code to reproduce the problem. Create a C# project, reference Dapper and open a connection to SQL Server (or other database), use .Query() to execute simple select query and retrieve the first row of result. Use GetType() to get the type of result object, the return value is null.
using (SqlConnection cn = new SqlConnection(csSql))
{
var rec = cn.Query("select getdate() as D").Single();
var t = rec.GetType(); // t == null
Console.WriteLine(t.Name); // null reference exception
}
I suspect that dynamic or private type is the cause of null, so I write my class library for test:
namespace Lib
{
public class Blah
{
public static dynamic SecretObject;
static Blah()
{
SecretObject = new PrivateType();
}
}
class PrivateType
{
}
}
In another project, get the dynamic type static field and call GetType():
dynamic obj = Lib.Blah.SecretObject;
Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
According to the test result, even cast private type as dynamic, I still can get the private type information from GetType(), why DapperRow.GetType() return null?
DapperRow is specifically built and utilized within Dapper to provide highly optimized row returns without reiterating header information. This is to help condense the size of the object and reduce redundant data, making it more efficient.
However, it would appear that the StackExchange team took the meta programming even further than a first glance would indicate.
DapperRow implements the System.Dynamic.IDynamicMetaObjectProvide interface, which requires that the GetMetaObject method be implemented:
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DapperRowMetaObject(parameter,
System.Dynamic.BindingRestrictions.Empty, this);
}
DapperRowMetaObject is a custom implementation of DynamicMetaObject that essentially hijacks and overrides what methods can be invoked against the dynamic type and what those calls should translate to. In this case, calls to anything other than the DapperRow's IDictionary.Item getter or the DapperRow.SetValue will fail since they are always routed to those two calls, but the value will be defaulted to null for any "get" calls where the target property does not exist in the table.
public bool TryGetValue(string name, out object value)
{
var index = table.IndexOfName(name);
if (index < 0)
{ // doesn't exist
value = null;
return false;
}
...
}
At that point, any methods invoked on a null dynamic value will throw a RuntimeBinderException:
RuntimeBinderException: Cannot perform runtime binding on a null
reference
You can easily test this hypothesis by replacing GetType() with another call that will throw the exact same exception:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
Keep in mind, the underlying type information of any properties on the dynamic object itself can still be accessed directly:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
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!
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 want to know if a type is IQueryable.
var t = typeof(IQueryable<int>);
bool isQueryable = (t is IQueryable); // false
bool isAssignableFrom = t.IsAssignableFrom(typeof(IQueryable)); // false
bool nameStartsWithIQueryable = t.Name.StartsWith("IQueryable"); // true
The third way - looking at the beginning of the type name - works but feels like a hack.
Is there another way to accomplish this?
Use GetGenericTypeDefinition:
bool isQueryable = t.GetGenericTypeDefinition() == typeof(IQueryable<>);
If you need to handle deeper ancestries (where you are checking aganist the base type), you can write a helper method:
public static bool IsType(Type type, Type ancestor)
{
while (type != null)
{
if (type.IsGenericType)
type = type.GetGenericTypeDefinition();
if (type == ancestor)
return true;
type = type.BaseType;
}
return false;
}
Calling it like so:
bool isQueryable = IsType(typeof(IQueryable<int>), typeof(IQueryable<>));
(Note that this won't help with base types that implement other interfaces, since checking against that would require a lot more code using Type.GetInterfaces recursively.)
You just have the IsAssignableFrom backwards, it should be:
typeof(IQueryable).IsAssignableFrom(t);
I prefer using IsAssignableFrom in the general case because when you're not dealing with interfaces, IsAssignableFrom will tell you true if a class as an implict user-defined conversion operator. Plus, you don't have to deal with type hierarchies, etc...