I've defined a "type translator" interface (please don't mention AutoMapper, not relevant) as follows:
public interface ITranslator<TSource, TResult>
{
TResult Translate(TSource source);
}
And for convenience sake, I've defined an extension method for enumerables:
public static class ExtendITranslator
{
public static IEnumerable<TResult> TranslateSet<TSource, TResult>(
this ITranslator<TSource, TResult> self,
IEnumerable<TSource> sourceSet)
{
return sourceSet.Select(o => self.Translate(o));
}
}
And then I define the implementation as so:
public class Translator :
ITranslator<Report, Report.ReportServiceModel>,
ITranslator<Report.ReportServiceModel, Report>
{
public Report Translate(Report.ReportServiceModel source)
{
throw new NotImplementedException();
}
public Report.ReportServiceModel Translate(Report source)
{
throw new NotImplementedException();
}
}
The problem is, when I work with the Translator implementation, it doesn't expose TranslateSet as an extension method, unless I cast:
Translator translator = new Translator();
// not exposed
translator.TranslateSet(/* ... */);
// exposed with a cast
(translator as ITranslator<Report, Report.ReportServiceModel>).TranslateSet(/* ... */);
Why is this? Other class implementations expose extension methods defined for the interfaces they implement:
public interface IUpdater<TModel> where TModel : class, new()
{
bool Update(TModel model);
}
public static class ExtendIRepositoryWriter
{
public static int UpdateSet<TModel>(
this IRepositoryWriter<TModel> self,
IEnumerable<TModel> modelSet) where TModel : class, new()
{
return modelSet.Count(o => self.Update(o));
}
}
public class ReportUpdater : IUpdater<Report>
{
public bool Update(Report model)
{
throw new NotImplementedException();
}
}
In this case, the UpdateSet extension method is available on instances of ReportUpdater:
ReportUpdater reportUpdater = new ReportUpdater();
// all good!
reportUpdater.UpdateSet(/* ... */);
Can anyone shed some light on this? Did I fat-finger a typo or something? (It is Friday night after all)
Your other working example is actually misleading and is not a true apples-to-apples comparison.
Due to a single generic argument, the compiler is able to infer the type since you are invoking on an instance of that type.
This is not the case with the extension giving you trouble, since there are two type arguments. You can resolve this by assigning the concrete type to an interface declaring the types.
ITranslator<Report, Report.ReportServiceModel> translator = new Translator();
translator.TranslateSet(/* ... */);
Related
We are currently working with generics on extensions for enumerable-derived classes.
Apparently, the compiler produces an error CS0411 with extension methods if they are called on a class derived from IEnumerable<T> and return values that are of the enclosed T type.
The following example reproduces the error:
public class Item { }
public class CollectionType : IEnumerable<Item>
{
public IEnumerator<Item> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public static class CollectionExtension
{
public static T[] DoSomething<T, TCollection>(this TCollection source)
where TCollection : IEnumerable<T>
{
throw new NotImplementedException();
}
}
public class Test
{
public void TestMethod()
{
var collection = new CollectionType();
collection.DoSomething();
collection.DoSomething<Item, CollectionType>(); // This works fine
}
}
Calling DoSomething() will produce the following error:
The type arguments for method 'T[] [...].CollectionExtension.DoSomething<T,TCollection>(this TCollection)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
How can this happen if TCollection is bound to be of type IEnumerable<T> and T is also defined as a generic type parameter of DoSomething()?
Calling the extension method on the CollectionType should provide DoSomething() with both generic types.
It would not be possible to use the DoSomething() method as-is without specifying the generic types explicitly. The class IEnumerable<T> from which it seemingly would be possible to infer the type of T is located in the type constraint declaration, unfortunately C# "...cannot infer the type parameters only from a constraint or return value". See the details here:
[https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-methods]
This is issue can be solved in a different ways, you could explicitly specify the type as you have done in the example code - collection.DoSomething<Item, CollectionType>(), or add to DoSomething() an argument of type T, in that case C# will be able to infer the type, or get rid of the generic type TCollection, replacing it with IEnumerable<T> in the following way:
public static T[] DoSomething<T>(this IEnumerable<T> source)
{
throw new NotImplementedException();
}
The last one I think would be a cleaner way.
I am writing a generic expander extension method that will operate on a collection of database object, and convert them and all selected sub-models to internal model objects.
I know EF can help with this, but not as it's currently used in the rest of the codebase I'm working with, and that's not the question anyway.
The question is: Is there any way to couple classes so generics will know if one is used, the other is implied as it's pair?
To explain...
I want to have a method
public static IEnumerable<TResult> ExpandAsModels<TSource,TResult>(this IQueryable<TSource> source)
that will handle type binding successfully so I don't have to fill in the template values every time.
i.e. instead of
var result = datasource.ExpandAsModel<DBCarObject,CarModel>();
I want to be able to use
var result = datasource.ExpandAsModel();
I know this sounds pedantic but I'm trying to produce code that junior coders won't stuff up.
What I want is to be able to associate SourceTypes and ResultTypes. For example, having an explicit association between DBCarObject and CarModel.
I tried doing this by making DBCarObject implement an interface IDBObject<CarModel> and defining the extension method as
public static IEnumerable<TResult> ExpandAsModels<TSource,TResult>(this IQueryable<TSource> source)
where TSource: IDBObject<TResult>
This works in that it prevents mismatched objects, but it doesn't automatch the generics (so you can't use the simple var result = datasource.ExpandAsModel(); form of call)
Can anyone explain if there is a way to do this?
There seem to be so many situations where types operate in related ways that could make use of this sort of association.
If I were designing C# I guess I'd be after a feature that might be used like...
class a {}
class b {}
class x {
associated a mymodel;
}
class y {
associated b mymodel;
}
TResult doSomething<TResult,TSource>(TSource data)
where TSource has association mymodel
where TResult : mymodel
This would then require that TSource was an object that had an association called mymodel
and that TResult would be of the type of mymodel define by the association.
The final result would be that doSomething(new x()); would have a return type of a and doSomething(new y()); would have a return type of b.
Either that, or have the compiler able to trace through generics as per my example above.
If you drop TSource from your extension method, the compiler can deduce the type arguments;
public interface IDBObject<TResult>
{
}
public static IEnumerable<TResult> ExpandAsModels<TResult>(this IQueryable<IDBObject<TResult>> source)
{
// TODO
}
public class ClientView {
}
public class Client : IDBObject<ClientView>
{
}
public static void Test() {
IQueryable<Client> query = null;
var result = query.ExpandAsModels();
}
If you need your extension method to have convenient access to the type TSource, you could make your generic interface recursive;
public interface IDBObject<TSource, TResult> where TSource : IDBObject<TSource, TResult>
{
}
public static IEnumerable<TResult> ExpandAsModels<TSource, TResult>(this IQueryable<IDBObject<TSource, TResult>> source) where TSource : IDBObject<TSource, TResult>
{
// TODO
}
public class Client : IDBObject<Client, ClientView>
{
}
Maybe something like this:
public abstract class SourceBase<TResult> where TResult : TargetBase
{
public abstract TResult MapToResult();
}
public class TargetBase
{
}
public class SourceA : SourceBase<TargetA>
{
public string SourceProperty { get; set; }
public override TargetA MapToResult()
{
// here maps the data to target;
var data = new TargetA();
data.TargetProperty = this.SourceProperty;
return data;
}
}
public class TargetA : TargetBase
{
public string TargetProperty { get; set; }
}
The extension:
public static TargetBase ExpandAsModels<TSource>(this IQueryable<TSource> source) where TSource : SourceA, new()
{
TSource data = new TSource();
// fill the data reading from db
// Maps and return the value
return data.MapToResult();
}
When use:
IQueryable<SourceA> MySourceData = ...
MySourceData.ExpandAsModels();
I have created this interface for my Repositories.
public interface IRepository<T, in TKey> where T: class
{
IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
IEnumerable<T> FindAll();
T FindSingle(TKey id);
void Create(T entity);
void Delete(T entity);
void Update(T entity);
}
The FindSingle method accepts an ID, which will be used for searching on Primary Key. By using in I expected that I would only be allowed to pass a reference type as TKey. Out of curiosity I decided to create a concrete class and specify it as an int, so I could see the exception.
I looked up MSDN and it specifies this should not work
Covariance and contravariance in generic type parameters are supported for reference types, but they are not supported for value types.
The class I created looks like this
public class ProjectRepository : IRepository<Project,int>
{
public IEnumerable<Project> Find(Expression<Func<Project, bool>> predicate)
{
throw new NotImplementedException();
}
public IEnumerable<Project> FindAll()
{
throw new NotImplementedException();
}
public Project FindSingle(int id)
{
throw new NotImplementedException();
}
public void Create(Project entity)
{
throw new NotImplementedException();
}
public void Delete(Project entity)
{
throw new NotImplementedException();
}
public void Update(Project entity)
{
throw new NotImplementedException();
}
}
Why did I not get an exception on build having specified TKey as a value type? Also, If I removed the in from my parameter what have I lost? the MSDN document says that the contravariance allows using a less derived type, but surely by removing in I can pass any type in as it is still generic.
This is maybe displaying a lack of understanding on contravariance and covariance but it has me a little confused.
Covariance and contravariance don't make as much sense on value types, because they are all sealed. Though it's not clear from the documentation, it is valid to use a struct as a co/contravariant type, it's just not always useful. The documentation you reference is most likely referring to that the following is not valid:
public struct MyStruct<in T>
Contravariance means that you can do something like the following example:
IRepository<string, Base> b = //something
IRepository<string, Derived> d = b;
Since there's nothing that derives from int, you can use an IRepository<string, int>, but only as an IRepository<string, int>.
Covariance means that you can do the reverse, e.g. IEnumerable<T> is out T, which is covariant. You can do the following:
IEnumerable<Derived> d = //something
IEnumerable<Base> b = d;
If you're trying to restrict both TKey and T to classes (reference types), you should include a second restriction:
public interface IRepository<T, in TKey>
where T : class
where TKey : class
Indeed, you are missing the whole point of co- and contravariance :-) It is about being able to assign a variable of a generic type to another variable of the same generic type but with differing generic type argument(s) that are related to the ones used in the source.
Depending on whether the generic type parameter is co- or contravariant, different assignments are allowed.
Assume the following interface:
public interface IRepository<in T>
{
void Save(T value);
}
Additionally, assume the following interface along with a value type and a reference type that implement it:
public interface IBar
{
}
public struct BarValueType : IBar
{
}
public class BarReferenceType : IBar
{
}
Finally, assume two variables:
IRepository<BarReferenceType> referenceTypeRepository;
IRepository<BarValueType> valueTypeRepository;
Contravariance now means that you can assign an instance of IRepository<IBar> to the variable referenceTypeRepository, because BarReferenceType implements IBar.
The section from the MSDN you quote simply means that the assignment of an instance of IRepository<IBar> to valueTypeRepository is not legal, although BarValueType also implements IBar.
There is no problem in implementing your interface with a value type. You will only get an error when trying to assign an IRepository<Project, object> to a IRepository<Project, int>, for example. In the following code, the last assignment won't compile:
public interface IContravariant<T, in TKey> where T : class
{
T FindSingle(TKey id);
}
public class objCV : IContravariant<Project, object>
{
public Project FindSingle(object id)
{
return null;
}
public static void test()
{
objCV objcv = new objCV();
IContravariant<Project, Project> projcv;
IContravariant<Project, int> intcv;
projcv = objcv;
intcv = objcv;
}
}
In this article, they are telling us that the type parameter is treated as invariant by the compiler:
Variance applies only to reference types; if you specify a value type
for a variant type parameter, that type parameter is invariant for the
resulting constructed type.
From: http://msdn.microsoft.com/en-us/library/dd799517.aspx
I have the following DBML modification (i'm using Linq to SQL as a DAL).
public interface ILinqSQLObject { }
// these are objects from SQL Server mapped into Linq to SQL
public partial class NEWDEBT : ILinqSQLObject { }
public partial class OLDDEBT : ILinqSQLObject { }
public partial class VIPDEBT : ILinqSQLObject { }
With that i can manipulate my Linq objects more properly on other areas.
I've just done an IRepository pattern implementation.
public interface IDebtManager<T>
{
IQueryable<T> GetAllDebts();
IQueryable T GetSpecificDebt(System.Linq.Expressions.Expression<Func<T, bool>> predicate);
void Insert(T debt);
// other methods
}
public class DebtManager<T> : IDebtManager<T> where T : class, ILinqSQLObject
{
DebtContext conn = new DebtContext();
protected System.Data.Linq.Table<T> table;
public DebtManager()
{
table = conn.GetTable<T>();
}
public void Insert(T debt)
{
throw new NotImplementedException();
}
public IQueryable<T> GetSpecificDebt(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return table.Where(predicate);
}
public IQueryable<T> GetAllDebts()
{
return table;
}
}
And that works flawlessly. But, there are sometimes that i don't know, on compilation time, which specific table i'll be using. For that i tried to create a simple generic factory for my DebtManager.
public static class DebtFactoryManager
{
public static DebtManager<ILinqSQLObject> GetDebtManager(string debtType)
{
switch (debtType)
{
case "New Client":
return new DebtManager<NEWDEBT>();
case "Old Client":
return new DebtManager<OLDDEBT>();
case "VIP Client":
return new DebtManager<VIPDEBT>();
default:
return new DebtManager<NEWDEBT>();
}
return null;
}
}
However it doesn't work. It says that i cannot 'implicity convert DebtManager<NEWDEBT> to DebtManager<ILinqSQLObject>', but if NEWDEBT implements ILinqSQLObject, why isn't the compiler recognizing it? Obviously i'm doing some mistake but i can't see it.
This error is caused by the fact that generics do not implicitly support covariance; that is, treating a specific generic parameter type as if it were one of its base types.
Couple ways around this. First, you can define a non-generic DebtManager base class that the generic DebtManager inherits from, and return that. Second, you can define a generic interface that DebtManager implements; generic interfaces CAN be defined to be covariant, by using the keyword out before the generic type parameter.
EDIT: Let's go back to the primary need. You may not know, at compile-time, what type of object you will be required to work with and therefore you don't know which Repository you need. Might I suggest, then, that instead of a Repository-per-table architecture, you use a Repository-per-database. DebtManager is already generic to any Linq to SQL type; why not then make the methods generic as well, allowing them to be generic from call to call?
public interface IRepository<T> where T:class, ILinqSqlObject
{
IQueryable<TSpec> GetAllDebts<TSpec>() where TSpec : T;
IQueryable<TSpec> GetSpecificDebt<TSpec>(System.Linq.Expressions.Expression<Func<TSpec, bool>> predicate) where TSpec : T;
void Insert<TSpec>(TSpec debt) where TSpec:T;
// other methods
}
interface IDebtObject : ILinqSqlObject
public interface IDebtManager:IRepository<IDebtObject> { }
public class DebtManager:IDebtManager
{
DebtContext conn = new DebtContext();
public DebtManager()
{
}
public void Insert<T>(T debt) where T:IDebtObject
{
throw new NotImplementedException();
}
public IQueryable<T> GetSpecificDebt(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T:IDebtObject
{
return conn.GetTable<T>().Where(predicate);
}
public IQueryable<T> GetAllDebts<T>() where T:IDebtObject
{
return conn.GetTable<T>();
}
}
I have a problem with casting a generic class to the interface it is implementing.
My code is like this:
interface foo
{
void foobar();
}
class bar: foo
{
public void foobar()
{
throw new NotImplementedException();
}
}
now I have my factory that creates instances of my classes by the interface, mostly a simple microkernel (service locator). I will simplify it here. Normally it will look up the implementing class from the configs and the factory take the type as T but that doesn't matter for the problem I have.
public static class Factory
{
public static Lazy<foo> CreateLazyInstance()
{
Lazy<foo> instance;
Type type = typeof(bar);
Type lazyType = typeof(Lazy<>);
Type toContruct = lazyType.MakeGenericType(type);
instance = (Lazy<foo>)Activator.CreateInstance(toContruct);
return instance;
}
}
If will fail at:
instance = (Lazy<foo>)Activator.CreateInstance(toContruct);
and claim with an InvalidCastException that it is not possible to cast the type Lazy<bar> to Lazy<foo>.
Is there any way to tell the CLR that this cast will work or to workaround this problem?
No - Lazy<T> is invariant - so a Lazy<string> is not a Lazy<object> for example. (As pointed out in comments, it couldn't be declared as covariant in T, as it's a class, not an interface or delegate.)
However, you can convert one to the other easily enough:
static Lazy<TOutput> CreateLazyProxy<TInput, TOutput>
(Lazy<TInput> input) where TInput : TOutput
{
return new Lazy<TOutput>(() => input.Value);
}
Also, Func<T> is covariant, so this will work too:
static Lazy<TOutput> CreateLazy<TInput, TOutput>(Func<TInput> func)
where TInput : TOutput
{
return new Lazy<TOutput>(func);
}
(Not that you particularly need a method for that - if you've got a Func<TInput>, just construct a Lazy<TOutput> directly.)
An easier way to do this would be to pass in a lambda to the Lazy constructor. So, your code would look like the following:
public static Lazy<foo> CreateLazyInstance()
{
Type type = typeof(bar);
return new Lazy<foo>(() => (foo)Activator.CreateInstance(type));
}
You must do your foo generic parameter : new():
public static Lazy<foo> CreateLazyInstance() where foo : new()
And change your code to find a constructor and call it:
Type t = typeof(foo);
t.GetConstructor(new type[]{});
return (foo)t.Invoke(new object[]{});